背景
前不久,因為公司業(yè)務需要,需要解決在大促場景下后端業(yè)務的熱點緩存問題,所以研究了下緩存熱點解決方案。很多公司的緩存都是基于redis來做的,redis的性能其實已經(jīng)足以能應付大部分的場景,但是對于大促期間或者活動搶購期間的某個爆品,可能會出現(xiàn)在幾秒時間內(nèi)流入大量的流量,由于某個爆品的數(shù)據(jù)在redis cluster場景下會按照hash規(guī)則被存放在某個redis分片上,那么這幾秒的流量都會壓到這個redis分片,從而在瞬間會導致這個redis分片的癱瘓,也會影響后續(xù)的redis請求的阻塞。還有個場景,就是公司并不是所有的服務端邏輯都有緩存。在流量起來的時候,這些熱key還是會壓到數(shù)據(jù)庫層面。導致壓力。解決方案
一般常見的解決方案就是增加二級緩存,對于熱點數(shù)據(jù)寫到jvm里一份。設置過期時間。但是什么時候設置,熱點如何探測,規(guī)則如何設置,過期時間設置多少。甚至于如何快速落地,這都是需要研究的問題。我們希望有一個統(tǒng)一的方案來解決這些問題。我們發(fā)現(xiàn)了Hotkey這款開源框架。Hotkey源于京東,hotkey能自動地對任意突發(fā)性的無法預知的熱點數(shù)據(jù),按照配置的規(guī)則進行毫秒級別的探測,探測到的熱數(shù)據(jù)會推送到所有的服務端JVM中,大幅減輕對后端數(shù)據(jù)層的沖擊。這些熱數(shù)據(jù)在整個微服務集群會保持一致性,當熱點消失的時候,自動從jvm中進行移除。Hotkey的特性能很好的實現(xiàn)我們的目標。并且京東內(nèi)部也用Hotkey實戰(zhàn)了618大促,穩(wěn)定性有所保障。Hotkey的架構(gòu)圖(以下圖引用自Hotkey在Gitee的主頁)Hotkey整個架構(gòu)共分為以下幾個部分:worker:負責采集上報信息,根據(jù)規(guī)則計算出熱點信息,規(guī)則來自于etcd。熱點信息推送到client里client:每個client連接etcd,獲取每個worker的ip和端口,和worker保持長鏈接,接受worker的熱點信息推送etcd:分布式的協(xié)調(diào)者,接受每個worker的心跳上報,并把worker的連接信息推送給client。監(jiān)聽規(guī)則的改變,推送給workerdashboard:ui界面,查看實例以及worker的狀態(tài),查看以及修改規(guī)則數(shù)據(jù)。規(guī)則存到mysql,同時由etcd推送給workerhotkey: 京東App后臺中間件,毫秒級探測熱點數(shù)據(jù),毫秒級推送至服務器集群內(nèi)存,大幅降低熱key對數(shù)據(jù)層查詢壓力 (gitee.com)關于Hotkey的介紹和如何搭建,大家可以看這篇文章來了解,這里就不多贅述。碰到的問題
我們在搭建hotkey環(huán)境和落地實施中,碰到2個問題:我們更希望提供一種侵入更少的方式,在RPC以及接口的層面進行代理包裝。使用者無論使用什么RPC框架,只是在相關接口上打上標注,而無需動業(yè)務的任何代碼。就可以在這個接口層面進行檢測熱點。如果該接口的某個參數(shù)為熱點的話,就自動進行代理,走jvm的熱點數(shù)據(jù),等熱點消除后,依舊走原來的調(diào)用。如果你覺得上述的描述過于難以理解的話,那么直白點說就是:比如某個活動大促期間有個商品S001進行搶購,有大量的流量進入了商品詳情頁面。這個商品詳情RPC方式調(diào)用了商品服務的以下接口方法獲取商品信息:public interface ProductService{
SkuInfo getSkuInfo(String skuCode);
}
那么我們希望只在這個接口上打上標注。就可以適配Hotkey框架進行探測熱點,當商品S001被大量請求時,S001這個商品就可以成為熱點,這時getSkuInfo這個接口就會被自動代理,從而只從Jvm中獲取數(shù)據(jù),而不會真正走RPC調(diào)用。等熱點消除后,這個接口依舊調(diào)用RPC獲取數(shù)據(jù)。這樣的方式無疑侵入性更小,更容易使Hotkey框架落地。Hotlink客戶端
為此我們基于Hotkey client研發(fā)了Hotlink客戶端框架,該客戶端框架能讓Hotkey更完美的落地,增強了Hotkey客戶端的能力。Hotlink的項目地址:hotlink: Hotlink框架是一個基于Hotkey框架的客戶端增強版實現(xiàn),該客戶端框架能讓Hotkey更完美的落地,增強了Hotkey客戶端的能力。(gitee.com)業(yè)務接入簡單,只需要一個標注,1分鐘就能使你的RPC接口接入熱點探測框架
啟動時動態(tài)掃描所有Hotlink標注的接口,創(chuàng)建動態(tài)代理
基于動態(tài)代理去對接口做增強,理論上只要有接口,就支持任何RPC框架
本地方法只要有接口,也能使用熱點探測
結(jié)合Hotkey的架構(gòu)圖,Hotlink在整個架構(gòu)圖中的位置如下圖:Hotlink如何使用
按照Hotkey的部署要求,搭建好worker和dashboard。具體方式請參照:hotkey: 京東App后臺中間件,毫秒級探測熱點數(shù)據(jù),毫秒級推送至服務器集群內(nèi)存,大幅降低熱key對數(shù)據(jù)層查詢壓力 (gitee.com)本地業(yè)務項目依賴jar包(此jar包并未上傳到中央倉庫,需要大家自己deploy到自己公司的私庫)<dependency>
<groupId>com.thebeastshop</groupId>
<artifactId>hotlink-spring-boot-starter</artifactId>
<version>1.0.12</version>
</dependency>
hotlink需要的fastjson和groovy版本有點要求,如果你項目中的這2個包版本過低又同時覆蓋了hotlink的傳遞依賴包時,需要額外指定版本:<fastjson.version>1.2.70</fastjson.version>
<guava.version>29.0-jre</guava.version>
本地springboot配置文件里加入?yún)?shù)#此app-name不配置的話,會優(yōu)先讀取spring.application.name屬性
hotlink.app-name=test
#etcd地址和端口
hotlink.etcd-url=http://xxx.xxx.xxx.xxx:2379
public interface ProductService{
@Hotlink
SkuInfo getSkuInfo(String skuCode);
}
那么當某一個SKU001成為熱點時,那么傳入?yún)?shù)SKU001會自動代理從JVM里取到數(shù)據(jù),而SKU002則繼續(xù)走RPC調(diào)用。使用Hotlink需要注意的事項
由于Hotlink的實現(xiàn)是用動態(tài)代理來實現(xiàn),只要滿足這兩個條件,即可在啟動時會掃描器掃到:接口層面上標注@Hotlink
相關實現(xiàn)會被注入Spring上下文中
在標注接口的時候,盡量標注在一定時間范圍內(nèi)是冪等的接口。比如會員查詢,sku信息查詢,相關活動信息的查詢,這些信息在一定時間范圍內(nèi)不會頻繁變動,那么就適合做熱點探測。非冪等性的接口,即便是相同參數(shù),每次返回也會不一樣。那就不建議做熱點探測。比如下單,庫存的查詢,余額的查詢。這樣的接口如果一旦被升級成熱點。那會影響業(yè)務界面的正確性和后續(xù)邏輯的判斷錯誤。
點擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動和交流,掃描下方”二維碼“或在“公眾號后臺“回復“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~