背景
前不久,因為公司業務需要,需要解決在大促場景下後端業務的熱點快取問題,所以研究了下快取熱點解決方案。
很多公司的快取都是基於redis來做的,redis的效能其實已經足以能應付大部分的場景,但是對於大促期間或者活動搶購期間的某個爆品,可能會出現在幾秒時間內流入大量的流量,由於某個爆品的資料在redis cluster場景下會按照hash規則被存放在某個redis分片上,那麼這幾秒的流量都會壓到這個redis分片,從而在瞬間會導致這個redis分片的癱瘓,也會影響後續的redis請求的阻塞。
還有個場景,就是公司並不是所有的服務端邏輯都有快取。在流量起來的時候,這些熱key還是會壓到資料庫層面。導致壓力。
解決方案
一般常見的解決方案就是增加二級快取,對於熱點資料寫到jvm裡一份。設定過期時間。但是什麼時候設定,熱點如何探測,規則如何設定,過期時間設定多少。甚至於如何快速落地,這都是需要研究的問題。
我們希望有一個統一的方案來解決這些問題。
我們發現了Hotkey這款開源框架。
Hotkey源於京東,hotkey能自動地對任意突發性的無法預知的熱點資料,按照配置的規則進行毫秒級別的探測,探測到的熱資料會推送到所有的服務端JVM中,大幅減輕對後端資料層的衝擊。這些熱資料在整個微服務叢集會保持一致性,當熱點消失的時候,自動從jvm中進行移除。
Hotkey的特效能很好的實現我們的目標。並且京東內部也用Hotkey實戰了618大促,穩定性有所保障。
Hotkey的架構圖(以下圖引用自Hotkey在Gitee的主頁)
Hotkey整個架構共分為以下幾個部分:
worker:負責採集上報資訊,根據規則計算出熱點資訊,規則來自於etcd。熱點資訊推送到client裡
client: 每個client連線etcd,獲取每個worker的ip和埠,和worker保持長連結,接受worker的熱點資訊推送
etcd:分散式的協調者,接受每個worker的心跳上報,並把worker的連線資訊推送給client。監聽規則的改變,推送給worker
dashboard:ui介面,檢視例項以及worker的狀態,檢視以及修改規則資料。規則存到mysql,同時由etcd推送給worker
下面給出hotkey的專案地址
關於Hotkey的介紹和如何搭建,大家可以看這篇文章來了解,這裡就不多贅述。
碰到的問題
我們在搭建hotkey環境和落地實施中,碰到2個問題:
- Hotkey雖然開源,但是相關client jar包並未上傳中央倉庫,dashboard和worker啟動包也並未提供下載。需要下載原始碼進行編譯,編譯過程中也碰到一些包依賴的問題。
- Hotkey的client jar只提供了api級別的方法供程式使用,如果要落地到業務專案中,需要大規模的修改程式碼才能實施。
我們更希望提供一種侵入更少的方式,在RPC以及介面的層面進行代理包裝。使用者無論使用什麼RPC框架,只是在相關介面上打上標註,而無需動業務的任何程式碼。就可以在這個介面層面進行檢測熱點。如果該介面的某個引數為熱點的話,就自動進行代理,走jvm的熱點資料,等熱點消除後,依舊走原來的呼叫。
如果你覺得上述的描述過於難以理解的話,那麼直白點說就是:
比如某個活動大促期間有個商品S001進行搶購,有大量的流量進入了商品詳情頁面。這個商品詳情RPC方式呼叫了商品服務的以下介面方法獲取商品資訊:
public interface ProductService{
SkuInfo getSkuInfo(String skuCode);
}
那麼我們希望只在這個介面上打上標註。就可以適配Hotkey框架進行探測熱點,當商品S001被大量請求時,S001這個商品就可以成為熱點,這時getSkuInfo這個介面就會被自動代理,從而只從Jvm中獲取資料,而不會真正走RPC呼叫。等熱點消除後,這個介面依舊呼叫RPC獲取資料。
這樣的方式無疑侵入性更小,更容易使Hotkey框架落地。
Hotlink客戶端
為此我們基於Hotkey client研發了Hotlink客戶端框架,該客戶端框架能讓Hotkey更完美的落地,增強了Hotkey客戶端的能力。
Hotlink的專案地址:https://gitee.com/openbeast/hotlink
該客戶端框架有以下特點:
- 業務接入簡單,只需要一個標註,1分鐘就能使你的RPC介面接入熱點探測框架
- 啟動時動態掃描所有Hotlink標註的介面,建立動態代理
- 基於動態代理去對介面做增強,理論上只要有介面,就支援任何RPC框架
- 本地方法只要有介面,也能使用熱點探測
結合Hotkey的架構圖,Hotlink在整個架構圖中的位置如下圖:
Hotlink如何使用
第一步
按照Hotkey的部署要求,搭建好worker和dashboard。具體方式請參照:
同時為了方便大家搭建,我把編譯好的worker和dashboard包也進行了上傳
worker下載地址:
公網IP版本(適合除錯用,本地能連上worker):
https://gitee.com/openbeast/hotlink/attach_files/813746/download/worker-0.0.4-SNAPSHOT-public.jar
內網IP版本:
https://gitee.com/openbeast/hotlink/attach_files/813747/download/worker-0.0.4-SNAPSHOT.jar
Dashboard:
https://gitee.com/openbeast/hotlink/attach_files/813749/download/dashboard-0.0.2-SNAPSHOT.jar
第二步
本地業務專案依賴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配置檔案里加入引數
#此app-name不配置的話,會優先讀取spring.application.name屬性
hotlink.app-name=test
#etcd地址和埠
hotlink.etcd-url=http://xxx.xxx.xxx.xxx:2379
第四步
在你的介面裡加入標籤@Hotlink
在介面上加:介面裡所有的方法都會自動探測熱點
在方法上加:只有這個方法會自動探測熱點
比如:
public interface ProductService{
@Hotlink
SkuInfo getSkuInfo(String skuCode);
}
那麼當某一個SKU001成為熱點時,那麼傳入引數SKU001會自動代理從JVM裡取到資料,而SKU002則繼續走RPC呼叫。
這樣就完成了所有的配置。啟動皆可。
使用Hotlink需要注意的事項
由於Hotlink的實現是用動態代理來實現,只要滿足這兩個條件,即可在啟動時會掃描器掃到:
- 介面層面上標註
@Hotlink
- 相關實現會被注入Spring上下文中
在標註介面的時候,儘量標註在一定時間範圍內是冪等的介面。比如會員查詢,sku資訊查詢,相關活動資訊的查詢,這些資訊在一定時間範圍內不會頻繁變動,那麼就適合做熱點探測。
非冪等性的介面,即便是相同引數,每次返回也會不一樣。那就不建議做熱點探測。比如下單,庫存的查詢,餘額的查詢。這樣的介面如果一旦被升級成熱點。那會影響業務介面的正確性和後續邏輯的判斷錯誤。
關於我
我是一個開源作者,也是一名內容創作者。「元人部落」是一個堅持做原創的技術科技分享號,會一直分享原創的技術文章,陪你一起成長。