一、引子
1-1. TMC 是什麼
TMC ,即“透明多級快取( Transparent Multilevel Cache )”,是有贊 PaaS 團隊給公司內應用提供的整體快取解決方案。
TMC 在通用“分散式快取解決方案(如 CodisProxy + Redis ,如有贊自研分散式快取系統 zanKV )”基礎上,增加了以下功能:
- 應用層熱點探測
- 應用層本地快取
- 應用層快取命中統計
以幫助應用層解決快取使用過程中出現的熱點訪問問題。
1-2. 為什麼要做 TMC
使用有贊服務的電商商家數量和型別很多,商家會不定期做一些“商品秒殺”、“商品推廣”活動,導致“營銷活動”、“商品詳情”、“交易下單”等鏈路應用出現 快取熱點訪問 的情況:
- 活動時間、活動型別、活動商品之類的資訊不可預期,導致 快取熱點訪問 情況不可提前預知;
- 快取熱點訪問 出現期間,應用層少數 **熱點訪問 key ** 產生大量快取訪問請求:衝擊分散式快取系統,大量佔據內網頻寬,最終影響應用層系統穩定性;
為了應對以上問題,需要一個能夠 自動發現熱點 並 將熱點快取訪問請求前置在應用層本地快取 的解決方案,這就是 TMC 產生的原因。
1-3. 多級快取解決方案的痛點
基於上述描述,我們總結了下列 多級快取解決方案 需要解決的需求痛點:
- 熱點探測:如何快速且準確的發現 **熱點訪問 key ** ?
- 資料一致性:前置在應用層的本地快取,如何保障與分散式快取系統的資料一致性?
- 效果驗證:如何讓應用層檢視本地快取命中率、熱點 key 等資料,驗證多級快取效果?
- 透明接入:整體解決方案如何減少對應用系統的入侵,做到快速平滑接入?
TMC 聚焦上述痛點,設計並實現了整體解決方案。以支援“熱點探測”和“本地快取”,減少熱點訪問時對下游分散式快取服務的衝擊,避免影響應用服務的效能及穩定性。
二、 TMC 整體架構
TMC 整體架構如上圖,共分為三層:
- 儲存層:提供基礎的kv資料儲存能力,針對不同的業務場景選用不同的儲存服務( codis / zankv / aerospike );
- 代理層:為應用層提供統一的快取使用入口及通訊協議,承擔分散式資料水平切分後的路由功能轉發工作;
- 應用層:提供統一客戶端給應用服務使用,內建“熱點探測”、“本地快取”等功能,對業務透明;
本篇聚焦在應用層客戶端的“熱點探測”、“本地快取”功能。
三、 TMC 本地快取
3-1. 如何透明
TMC 是如何減少對業務應用系統的入侵,做到透明接入的?
對於公司 Java 應用服務,在快取客戶端使用方式上分為兩類:
- 基於
spring.data.redis
包,使用RedisTemplate
編寫業務程式碼; - 基於
youzan.framework.redis
包,使用RedisClient
編寫業務程式碼;
不論使用以上那種方式,最終通過JedisPool
建立的Jedis
物件與快取服務端代理層做請求互動。
TMC 對原生jedis包的JedisPool
和Jedis
類做了改造,在JedisPool初始化過程中整合TMC“熱點發現”+“本地快取”功能Hermes-SDK
包的初始化邏輯,使Jedis
客戶端與快取服務端代理層互動時先與Hermes-SDK
互動,從而完成 “熱點探測”+“本地快取”功能的透明接入。
對於 Java 應用服務,只需使用特定版本的 jedis-jar 包,無需修改程式碼,即可接入 TMC 使用“熱點發現”+“本地快取”功能,做到了對應用系統的最小入侵。
3-2. 整體結構
3-2-1. 模組劃分
TMC 本地快取整體結構分為如下模組:
- Jedis-Client: Java 應用與快取服務端互動的直接入口,介面定義與原生 Jedis-Client 無異;
- Hermes-SDK:自研“熱點發現+本地快取”功能的SDK封裝, Jedis-Client 通過與它互動來整合相應能力;
- Hermes服務端叢集:接收 Hermes-SDK 上報的快取訪問資料,進行熱點探測,將熱點 key 推送給 Hermes-SDK 做本地快取;
- 快取叢集:由代理層和儲存層組成,為應用客戶端提供統一的分散式快取服務入口;
- 基礎元件: etcd 叢集、 Apollo 配置中心,為 TMC 提供“叢集推送”和“統一配置”能力;
3-2-2. 基本流程
1) key 值獲取
- Java 應用呼叫 Jedis-Client 介面獲取key的快取值時,Jedis-Client 會詢問 Hermes-SDK 該 key 當前是否是 熱點key;
- 對於 熱點key ,直接從 Hermes-SDK 的 熱點模組 獲取熱點 key 在本地快取的 value 值,不去訪問 快取叢集 ,從而將訪問請求前置在應用層;
- 對於非 熱點key ,Hermes-SDK 會通過
Callable
回撥 Jedis-Client 的原生介面,從 快取叢集 拿到 value 值; - 對於 Jedis-Client 的每次 key 值訪問請求,Hermes-SDK 都會通過其 通訊模組 將 key訪問事件 非同步上報給 Hermes服務端叢集 ,以便其根據上報資料進行“熱點探測”;
2)key值過期
- Java 應用呼叫 Jedis-Client 的
set()
del()
expire()
介面時會導致對應 key 值失效,Jedis-Client 會同步呼叫 Hermes-SDK 的invalid()
方法告知其“ key 值失效”事件; - 對於 熱點key ,Hermes-SDK 的 熱點模組 會先將 key 在本地快取的 value 值失效,以達到本地資料強一致。同時 通訊模組 會非同步將“ key 值失效”事件通過 etcd叢集 推送給 Java 應用叢集中其他 Hermes-SDK 節點;
- 其他Hermes-SDK節點的 通訊模組 收到 “ key 值失效”事件後,會呼叫 熱點模組 將 key 在本地快取的 value 值失效,以達到叢集資料最終一致;
3)熱點發現
- Hermes服務端叢集 不斷收集 Hermes-SDK上報的 key訪問事件,對不同業務應用叢集的快取訪問資料進行週期性(3s一次)分析計算,以探測業務應用叢集中的熱點key列表;
- 對於探測到的熱點key列表,Hermes服務端叢集 將其通過 etcd叢集 推送給不同業務應用叢集的 Hermes-SDK 通訊模組,通知其對熱點key列表進行本地快取;
4)配置讀取
- Hermes-SDK 在啟動及執行過程中,會從 Apollo配置中心 讀取其關心的配置資訊(如:啟動關閉配置、黑白名單配置、etcd地址...);
- Hermes服務端叢集 在啟動及執行過程中,會從 Apollo配置中心 讀取其關心的配置資訊(如:業務應用列表、熱點閾值配置、 etcd 地址...);
3-2-3. 穩定性
TMC本地快取穩定性表現在以下方面:
- 資料上報非同步化:Hermes-SDK 使用
rsyslog技術
對“ key 訪問事件”進行非同步化上報,不會阻塞業務; - 通訊模組執行緒隔離:Hermes-SDK 的 通訊模組 使用獨立執行緒池+有界佇列,保證事件上報&監聽的I/O操作與業務執行執行緒隔離,即使出現非預期性異常也不會影響基本業務功能;
- 快取管控:Hermes-SDK 的 熱點模組 對本地快取大小上限進行了管控,使其佔用記憶體不超過 64MB(LRU),杜絕 JVM 堆記憶體溢位的可能;
3-2-4. 一致性
TMC 本地快取一致性表現在以下方面:
- Hermes-SDK 的 熱點模組 僅快取 熱點key 資料,絕大多數非熱點 key 資料由 快取叢集 儲存;
- 熱點key 變更導致 value 失效時,Hermes-SDK 同步失效本地快取,保證 本地強一致;
- 熱點key 變更導致 value 失效時,Hermes-SDK 通過 etcd叢集 廣播事件,非同步失效業務應用叢集中其他節點的本地快取,保證 叢集最終一致;
四、TMC熱點發現
4-1. 整體流程
TMC 熱點發現流程分為四步:
- 資料收集:收集 Hermes-SDK 上報的 key訪問事件;
- 熱度滑窗:對 App 的每個 Key ,維護一個時間輪,記錄基於當前時刻滑窗的訪問熱度;
- 熱度匯聚:對 App 的所有 Key ,以
<key,熱度>
的形式進行 熱度排序彙總; - 熱點探測:對 App ,從 熱Key排序彙總 結果中選出 TopN的熱點Key ,推送給 Hermes-SDK;
4-2. 資料收集
Hermes-SDK 通過本地rsyslog
將 key訪問事件 以協議格式放入 kafka ,Hermes服務端叢集 的每個節點消費 kafka 訊息,實時獲取 key訪問事件。
訪問事件協議格式如下:
- appName:叢集節點所屬業務應用
- uniqueKey:業務應用 key訪問事件 的 key
- sendTime:業務應用 key訪問事件 的發生時間
- weight:業務應用 key訪問事件 的訪問權值
Hermes服務端叢集 節點將收集到的 key訪問事件 儲存在本地記憶體中,記憶體資料結構為Map<String, Map<String, LongAdder>>
,對應業務含義對映為Map< appName , Map< uniqueKey , 熱度 >>
。
4-3. 熱度滑窗
4-3-1. 時間滑窗
Hermes服務端叢集 節點,對每個App的每個 key ,維護了一個 時間輪:
- 時間輪中共10個 時間片,每個時間片記錄當前 key 對應 3 秒時間週期的總訪問次數;
- 時間輪10個時間片的記錄累加即表示當前 key 從當前時間向前 30 秒時間視窗內的總訪問次數;
4-3-2. 對映任務
Hermes服務端叢集 節點,對每個 App 每3秒 生成一個 對映任務 ,交由節點內 “快取對映執行緒池” 執行。對映任務 內容如下:
- 對當前 App ,從
Map< appName , Map< uniqueKey , 熱度 >>
中取出 appName 對應的MapMap< uniqueKey , 熱度 >>
; - 遍歷
Map< uniqueKey , 熱度 >>
中的 key ,對每個 key 取出其熱度存入其 時間輪 對應的時間片中;
4-4. 熱度匯聚
完成第二步“熱度滑窗”後,對映任務 繼續對當前 App 進行“熱度匯聚”工作:
- 遍歷 App 的 key ,將每個 key 的 時間輪 熱度進行彙總(即30秒時間視窗內總熱度)得到探測時刻 滑窗總熱度;
- 將
< key , 滑窗總熱度 >
以排序集合的方式存入 Redis儲存服務 中,即 熱度匯聚結果;
4-5. 熱點探測
- 在前幾步,每3秒 一次的 對映任務 執行,對每個 App 都會產生一份當前時刻的 熱度匯聚結果 ;
- Hermes服務端叢集 中的“熱點探測”節點,對每個 App ,只需週期性從其最近一份 熱度匯聚結果 中取出達到熱度閾值的 TopN 的 key 列表,即可得到本次探測的 熱點key列表;
TMC 熱點發現整體流程如下圖:
4-6. 特性總結
4-6-1. 實時性
Hermes-SDK基於rsyslog + kafka 實時上報 key訪問事件。 對映任務 3秒一個週期完成“熱度滑窗” + “熱度匯聚”工作,當有 熱點訪問場景 出現時最長3秒即可探測出對應 熱點key。
4-6-2. 準確性
key 的熱度匯聚結果由“基於時間輪實現的滑動視窗”匯聚得到,相對準確地反應當前及最近正在發生訪問分佈。
4-6-3.擴充套件性
Hermes服務端叢集 節點無狀態,節點數可基於 kafka 的 partition 數量橫向擴充套件。
“熱度滑窗” + “熱度匯聚” 過程基於 App 數量,在單節點內多執行緒擴充套件。
五、TMC實戰效果
5-1. 快手商家某次商品營銷活動
有贊商家通過快手直播平臺為某商品搞活動,造成該商品短時間內被集中訪問產生訪問熱點,活動期間 TMC 記錄的實際熱點訪問效果資料如下:
5-1-1. 某核心應用的快取請求&命中率曲線圖
- 上圖藍線為應用叢集呼叫
get()
方法訪問快取次數 - 上圖綠線為獲取快取操作命中 TMC 本地快取的次數
- 上圖為本地快取命中率曲線圖
可以看出活動期間快取請求量及本地快取命中量均有明顯增長,本地快取命中率達到近 80% (即應用叢集中 80% 的快取查詢請求被 TMC 本地快取攔截)。
5-1-2. 熱點快取對應用訪問的加速效果
- 上圖為應用介面QPS曲線
- 上圖為應用介面RT曲線
可以看出活動期間應用介面的請求量有明顯增長,由於 TMC 本地快取的效果應用介面的 RT 反而出現下降。
5-2. 雙十一期間部分應用 TMC 效果展示
5-2-1. 商品域核心應用效果
5-2-2. 活動域核心應用效果
六、TMC功能展望
在有贊, TMC 目前已為商品中心、物流中心、庫存中心、營銷活動、使用者中心、閘道器&訊息等多個核心應用模組提供服務,後續應用也在陸續接入中。
TMC 在提供“熱點探測” + “本地快取”的核心能力同時,也為應用服務提供了靈活的配置選擇,應用服務可以結合實際業務情況在“熱點閾值”、“熱點key探測數量”、“熱點黑白名單”維度進行自由配置以達到更好的使用效果。
最後, TMC 的迭代還在持續進行中...