首先,看壹下緩存條目緩存的get方法。JdHotKeyStore.getValue是獲取熱鍵的方法,它會報告訪問次數。如果獲取的熱鍵不為空,則直接返回。否則將從redis獲取並調用JdHotKeyStore.smartSet來判斷是否有熱鍵。如果有,它會設置壹個值,最後返回。
JdHotKeyStore.getValue會先調用inRule檢查這個鍵是否有對應的規則,如果沒有對應的規則就不處理,然後調用getValueSimple從本地內存中獲取hotkey的存儲對象ValueModel,如果沒有則調用hotkey presser . push開始計數;如果獲得,它會調用isNearExpire來判斷是否即將過期。如果是,它也會計數,然後取出ValueModel中的值是否有對應的值,只返回。最後,調用keyhandlerfactory。getcounter()。收集以統計相應的規則。我們壹步步來分析這個過程。
InRule會從KeyRule緩存中獲取相應的規則,在逐層調用後,會獲取KeyRuleHolder的findByKey方法,然後繼續調用其findRule方法選擇相應的KeyRule。如果沒有KeyRule,則直接返回,否則得到其時長(熱鍵緩存時間)和對應時長的本地緩存。實際上,對於方法的普適性,是用get代替contain的判斷。
findRule的邏輯比較特殊,作者留了評論,優先級全匹配-& gt;前綴匹配-& gt;*通配符,這是為了更準確地選擇相應的規則。比如配置了sku_的前綴規則,但是茅臺sku的流量突然上升,茅臺sku的本地緩存需要稍微長壹點的時間才能讓系統平穩度過高峰期,那麽就配置壹個sku_moutai_sku_id的全匹配規則,以免幹擾其他sku的緩存規則。
那麽KEY_RULES規則是從哪裏來的呢?這就要說到etcd了。其實etcd可以看作是zookeeper,也有配置crud然後通知客戶端的功能。這裏是定時拉+監控變化的雙重保障,和攜程阿波羅的處理很像:不要把雞蛋放在壹個籃子裏,底層功能真的很重要。每隔5秒定期從etcd中提取規則。打開監聽器,有變化就去etcd拉規則。FetchRuleFromEtcd從ectd的rule_path中獲取規則,然後轉換成ruleList繼續調用notifyRuleChange進行本地處理。
NotifyRuleChange會將KeyRuleInfoChangeEvent的通知發送到EventBus,然後進入KeyRuleHolder的putRules方法,在這裏可以看到KEY_RULES和RULE_CACHE_MAP被維護。
回到原來的流程,getValueSimple方法的鏈接比較長,主要是通過key的規則,獲取對應的時長,然後從對應時長的本地緩存中獲取ValueModel。
接下來是HotKeyPusher.push,如果是remove,在etcd中創建壹個節點然後刪除,達到集群刪除的效果。如果它是壹個探測器,並且鍵在規則內,則調用KeyHandlerfactory。GetCollector()。收集用於統計。
keyhandlerfactory工廠。getcollector()。collect方法交替使用兩個貼圖來累積計數,因此在清理貼圖時無需暫停。交替使用是避免停頓的有效方法。
除此之外,還有壹個keyhandlerfactory。getcounter()。collect即收集規則的訪問次數,也獲取相應的規則,然後累計規則的總訪問次數和熱門次數。
已經分析了兩個指標的集合,那麽如何將它們發送給worker呢?來到PushSchedulerStarter,會啟動壹個兩個指標的定時線程池,分別定時調用NettyKeyPusher的send和sendCount方法。
NettyKeyPusher的send和sendCount方法是為統計數據選擇對應的worker,然後發出請求。chooseChannel根據密鑰散列到壹個workers,然後發送壹個請求。
最後,當工作者統計熱鍵時,客戶端需要接收工作者推送的熱鍵並存儲。可以看到,NettyClientHandler會將ReceiveNewKeyEvent事件發送到EventBus,ReceiveNewKeyListener收到該事件後會調用receiveNewKeyListener.newKey,並將熱鍵放入本地緩存,客戶端的處理流程就結束了。
從上面可以看出,客戶端和worker的交互只是將統計數據推送給worker,worker接收並處理,最後將熱鍵推送給客戶端。所以工人端只需要分析兩個部分:統計數據匯總和推熱鍵。
首先,我們看到熱鍵的處理邏輯在HotKeyFilter中。首先,我們將累計totalReceiveKeyCount,然後調用publishMsg。如果統計信息超時1秒或者在白名單中,我們就不處理,否則繼續調用keyProducer.push。
KeyProducer.push將未過期的統計信息放入隊列。
worker端會打開指定數量的KeyConsumer,不斷消耗隊列中的統計數據。根據統計數據的類型調用KeyListener的removeKey和newKey。
KeyListener的removeKey和newKey方法在緩存中刪除或累積SlidingWindow。當刪除或達到壹定數量的訪問,它將被推送到所有客戶端選擇根據appname。
JD。COM的熱鍵處理是通過計數動態判斷是否是熱鍵,然後緩存在本地內存中,實現毫秒級的向外擴展。還有其他解決方法嗎?以下是我的看法:
1.如果面對壹些緩存鍵很少的場景,比如活動頁面信息(同時活動頁面數不能超過1000),可以直接把緩存放在本地內存,刷新時從redis拉最新的緩存即可,不需要動態計算熱鍵。也就是常見的多級緩存。
2.熱鍵也是動態判斷的,但是會遷移到壹個特殊的熱鍵redis集群,節點更多,性能更高。集群中的每個節點都有相同的熱鍵緩存,這樣可以分散請求,防止流量流向同壹個redis節點。如果是判斷熱鍵,則是從熱鍵簇中取出,而不是存儲在本地內存中,維護起來會更容易。