分佈式環境下對部分熱數據(如redis熱key,熱請求)進行探測,並對探測結果及時同步到各個client實例的JVM內存的方案簡述

可先閱讀之前的這篇,有讚的熱key探測及緩存方案。

常見場景

突發性的無法預先感知的熱點數據請求,或者有陣發性明顯熱點數據的。

譬如突然大量請求都命中了redis的某個分片,造成該redis卡頓,影響其他請求。熱key特性如 goodsId=100,突發1萬請求該key。

譬如突然大量同一個用戶的請求某一個或多個接口,呈現出攻擊性訪問的。熱key特性如userId-99= /cart,/cartAdd,....。

目標

大幅降低熱數據對下游服務(如redis、mysql)的衝擊,在極短時間內探測出熱點數據並緩存到jvm內存中。
小幅佔用內存容量,不影響性能,隨着熱度過去後,釋放佔用的內存。

流程步驟

 ———————————————Client端—————————————-

### 接受到熱key新增和刪除事件,來自於worker


有一個interface,包含兩個方法:
void newHotKey(HotKeyModel model);
void removeHotKey(HotKeyModel model);
HotKeyModel:對熱key的包裝類型
1 應包含createTime(時間戳)代表該key被探測出的時間
2 應包含key的名字,如 /queryGoods?goodsId=1
3 應包含appName,如userService
4 應包含該key的類型,如blackUserList,如hotRequest,如redisKey

直接實現該接口有諸多不便,應提供一個抽象類。對key的時間做一些校驗,譬如已經明顯過期的,就不要下發了。對於本地已存在的key,可以進行刷新過期時間的操作。對於不存在的key,進行新增操作。過期時間應該是可配置的,已熱的key再次收到時,就刷新過期時間。
對於remove事件,應該在本地進行刪除。刪除事件很重要,有可能worker發了刪除事件了,但是客戶端沒收到。所以需要雙重發送,客戶端也要監聽etcd的key刪除事件,再次確保已經刪除了該key。
譬如可以封裝一個cache接口,有add,remove,setExpire等方法。至於本地如何實現,是用caffeine還是guava cache,就不管了。

務必有etcd監聽的原因,譬如5個worker,都能連上etcd,但是有一個就是連不上client,導致無法推送給他。他的key也發不到worker去。就得靠監聽了。所以熱key推送到client後,還要推到etcd一份,client再監聽etcd的變化,決定是否也拉入自己的內存。那這就要求,每個推送事件有個唯一id


注意:
以上功能完成後,只是在本地保存了key,並沒有保存value。所以還需要客戶端來做一個aop切面,如果查詢的對象在熱key集合中,則獲取到的value應該賦值給熱key,尤其是redis的熱key。
當然也有一些場景下,不需要value,譬如黑名單,熱key直接就是userId-1001,只需要判斷存在該熱key即可,不需要value了。

### 和master交互的事件(待定,打算用ETCD)


客戶端會配置所有的master地址,如master1、master2、master3並放入內存中。各個master間是無狀態的,不設置master、slave之分,客戶端根據自己的ip或者什麼的hash一下,然後決定自己主要連哪個master,其他的master只維持個低頻心跳即可。(master之間的數據強一致性,由master和worker之間維護)
連上後,可能的事件彙總:
1》連接某個master,維持心跳。首次連接,拉取所有worker信息到本地,保存爲一個Array<WorkerInfo>。
2》連接被斷開,或心跳超時無反應,斷開與該master的連接,清除channel,並迅速切換下一個master,並拉取該master的全量信息(會不會卡住?)之後可以適當的搞個定時任務去拉取master緩存的信息。如果沒有下一個master了,那就不管master了,做好重試即可,等master恢復了再次連接。不影響和worker的通信。
3》master重連事件,有兩種情況,一種是當前在工作的master重連成功(譬如共1個master),另一種是非當前工作的master重連成功。List<MasterInfo>,masterInfo裏有是否active的標誌。
4》worker新增事件,由master推送過來WorkerAddEvent<array[workerInfo]>,然後建立起與新worker的連接,進行rehash。該新worker的index位置需要注意一下,Array[]比較好,當某個位置爲空時,就可以填補上去。
5》worker掉線事件,由master推送過來並且自己也連不上該worker了就切掉該worker的channel(如果只是自己連不上了),並且Array<WorkerInfo>裏將它的位置留空,置爲null,將來要發給該worker的信息,都發到index+1那個worker去。

### 如果把master替換爲ETCD

啓動所有worker後,客戶端連接etcd集羣,etcd本身基於raft算法維持強一致性,從etcd拉取所有worker信息,並保存到本地內存中。
然後監聽worker的新增和掉線事件,和上面差不多。
Worker在計算出熱key後,會發往所有客戶端,也會往etcd寫一份,etcd裏和client一樣的數據。當有新client加入時,可以從etcd裏獲取初始化所有的熱key。
Worker那裏可以開個口子,允許別的想監聽熱key事件的端,也能監聽到,譬如可以有個控制檯來做保存,做大屏展示。提供一個filter鏈的形式,先通知所有的client,再通知這些額外的。

### 上報熱key信息


提供三種模式:
1 tcp直連每次發送,適應於量不大時
2 tcp分批發送,每0.5秒或1秒發送一次
3 發送到MQ,讓worker去消費。
提供一個頂級接口,IUpload
包含一個方法,send(Map<appName, List<KeyModel> list> map),appName就是應用名,list就是要探測的key的集合。
KeyModel即是對要探測的key的封裝,包含了name,createTime,count等信息。

客戶端需要根據配置文件,來決定是否要上傳key信息,和上傳哪些key信息,如只上傳某些前綴開頭的、不上傳哪些前綴開頭的。配置信息可以從etcd拉取,worker也會從etcd拉取配置信息。兩端都要監聽etcd配置信息變化。

爲三種不同的模式,提供一個抽象類,並提供三個實現類。
提供一個filter過濾器,來過濾那些不該被上傳的key信息。之後開始上傳key信息。

### 統計功能


對JVM緩存命中率進行統計

### 應善用@Condition,提供默認的處理器
同時也給覆蓋提供方法

# ——————————Worker端——————————


整體應採用觀察者的模式,用eventBus進行解耦,各個內部事件監聽器只管監聽與自己相關的事件就好
1 接收到key事件,進行分發給不同線程
2 推送key事件, 先推appName下所有channel,再推額外的那些監聽者,譬如etcd和其他的如界面控制檯用來保存、監控、統計
3 監聽etcd事件


### 接收客戶端連接事件


接到客戶端連接後,保存到一個map裏,Map<appName, List<Channel>> map;將來推送時,也是根據appName推送的。
之後客戶端維持了與worker的連接和心跳,如果心跳不到位,就幹掉這個channel。


### 接收客戶端發來的key事件


有2種模式
1 tcp發來的
2 消費MQ得來的
接到之後採用distruptor進行分發,多線程計算。
一旦計算完畢某個key的熱情況,就可以刪掉該key的信息了。
對於那些超時不熱的,也要刪掉。

### 監聽etcd裏客戶端的配置變化信息


客戶端會配置各種規則,rule,並且可能會動態改變。
改變後,worker也要相應地改變,譬如客戶端修改了某個前綴不再參與計算。那worker也要相應地改變。雖然可能客戶端已經不再發來被屏蔽的key了,但是自己也控制一下。

 

 ——————————控制檯端——————————


主要功能:
1 提供控制檯界面
2 查看所有在線的app,在線的worker
3 連接etcd,增刪改查各客戶端的配置信息,按app維度
4 監控熱key情況(通過監聽etcd,或從worker那開個口子)
5 查看客戶端熱key命中次數
6 日誌查看

worker端:(如推送熱key到客戶端 — time)
client 端:如接收到熱key----time
如worker新增、失連

控制檯是數據落地的
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章