深入剖析分布式监控 CAT —— 消息文件存储
項目簡介
CAT(Central Application Tracking),是基于 Java 開發的分布式實時監控系統。CAT 目前在美團點評的產品定位是應用層的統一監控組件,在中間件(RPC、數據庫、緩存、MQ 等)框架中得到廣泛應用,為各業務線提供系統的性能指標、健康狀況、實時告警等。
CAT 目前在美團點評已經基本覆蓋全部業務線,每天處理的消息總量 3200 億+,存儲消息量近 400TB,在通信、計算、存儲方面都遇到了很大的挑戰。
感興趣的朋友歡迎 Star 開源項目?https://github.com/dianping/cat。
消息模型
組織關系
消息模型UML圖
消息類型
| Transaction | 記錄一段代碼的執行時間和次數 | 1. 執行時間較長的業務邏輯監控。 2. 記錄完整調用過程。 |
| Event | 記錄一段代碼的執行次數或事件是否發生 | 統計計數或異常事件記錄 |
| Metric | 記錄一個業務指標的變化趨勢 | 業務指標的發生次數、平均值、總和,例如商品訂單。 |
| Heartbeat | 定期上報數據或執行某些任務 | 定期上報統計信息,如 CPU 利用率、內存利用率、連接池狀態等。 |
埋點示例
public?void?shopInfo()?{Transaction?t1?=?Cat.newTransaction("URL",?"/api/v1/shop");try?{Transaction?t2?=?Cat.newTransaction("Redis",?"getShop");String?result?=?getCache();t2.complete();if?(result?!=?null)?{Cat.logEvent("CacheHit",?"Success");}?else?{Cat.logEvent("CacheHit",?"Fail");}Transaction?t3?=?Cat.newTransaction("Rpc",?"Call");try?{doRpcCall();}?catch?(Exception?e)?{t3.setStatus(e);Cat.logError(e);}?finally?{t3.complete();}}?catch?(Exception?e)?{t1.setStatus(e);Cat.logError(e);}?finally?{t1.complete();} }private?String?getCache()?throws?InterruptedException?{Thread.sleep(10);?//?mock?cache?durationreturn?null; }private?void?doRpcCall()?{throw?new?RuntimeException("rpc?call?timeout");?//?mock?rpc?timeout }LogView 消息樹
LogView 不僅可以分析核心流程的性能耗時,而且可以幫助用戶快速排查和定位問題。例如上述埋點示例對應的 LogView:
-
Transation 消息是可嵌套的。
-
logError 可記錄異常堆棧,是一種特殊的 Event 消息。
分布式調用鏈路
分布式logview示例CAT 可以提供簡單的分布式鏈路功能,典型的場景就是 RPC 調用。例如客戶端 A 調用服務端 B,客戶端 A 會生成 2 個 MessageID:表示客戶端 A 調用過程的 MessageID-1 和表示服務端 B 執行過程的 MessageID-2,MessageID-2 在客戶端 A 發起調用的時候傳遞給服務端 B,MessageID-2 是 MessageID-1 的兒子節點。
消息流水線
消息流水線如上圖所示,實時報表分析是整個監控系統的核心,CAT 服務端接收客戶端上報的原始數據,分發到不同類型的 Analyzer 線程中,每種類型的任務由一組 Analyzer 線程構成。由于原始消息的數量龐大,所以需要對數據進行加工、統計后生成豐富的報表,滿足業務方排查問題以及性能分析的需求。
其中 Logview 的 Analyzer 線程是本文討論的重點,它會收集全量的原始消息,并實時寫入磁盤,類似實現一個高吞吐量的簡易版消息系統。此外需要具備一定限度的隨機讀能力,方便業務方定位問題發生時的“案發現場”。
對于歷史的 Logview 文件會異步上傳至 HDFS。
消息文件存儲
CAT 針對消息寫多讀少的場景,設計并實現了一套文件存儲。以小時為單位進行集中式存儲,每個小時對應一個存儲目錄,存儲文件分為索引文件和數據文件。用戶可以根據 MessageID 快讀定位到某一個消息。
消息 ID 設計
CAT 客戶端會為每個消息樹都會分配唯一的 MessageID,MessageID 總共分為四段,示例格式:shop.service-0a010101-431699-1000。
-
第一段是應用名shop.service。
-
第二段是客戶端機器 IP 的16進制碼,0a010101 表示10.1.1.1。
-
第三段是系統當前時間除以小時得到的整點數,431699 代表 2019-04-01 19:00:00。
-
第四段是客戶端機器當前小時消息的連續遞增號。(存儲設計的重要依據點)
文件存儲 V1.0
總體概貌
V1.0 版本的文件存儲設計比較簡單粗暴,每個客戶端 IP 節點對應分別對應一個索引文件和數據文件。
消息存儲V1.0總體概貌單個 IP 視角
單個IP視角-
每個索引內容由存儲塊首地址和塊內偏移地址組成,共 6byte。
-
每個索引內容的序號與消息序列號一一對應,因為消息序列號是連續遞增號,所以索引文件基本可以保證是順序寫。
-
為了減少磁盤IO交互和寫入時間,消息采用批量 Gzip 壓縮后順序 append 至數據文件。
優缺點分析
| 1. 簡單易懂,實現復雜度不高。 2. 根據消息序列號可快速定位索引。 | 1. 隨著業務規模不斷擴展,存儲文件的數量并不可控。 2. 數據文件節點過多導致隨機 IO 惡化,機器 Load 飆高。 |
文件存儲 V2.0
V2.0 文件存儲進行了重新設計,以解決 V2.0 數據文件節點過多以及隨機 IO 惡化的問題。
總體概貌
消息存儲V2.0總體概貌V2.0 核心設計思想:
-
合并同一個應用的所有 IP 節點。
-
引入多級索引,建立 IP、Index、DataOffset 的映射關系。
-
同一個 IP 的索引數據盡可能保證順序存儲。
單個索引文件視角
單個索引文件視角索引文件存儲的特點:
-
需要根據 IP + Index 建立一級索引。
-
不同 IP 節點跳躍式存儲,每次劃分一段連續且固定大小的存儲空間。
-
同一個 IP 節點根據 Index 在每塊固定大小的存儲空間內順序存儲。
最小索引單元視角
最小索引單元視角上圖是索引結構的最小單元,每個索引文件由若干個最小單元組成。每個單元分為 4 * 1024 個 Segment,第一個 Segment 作為我們的一級索引 Header,存儲 IP、消息序列號與 Segment 的映射信息。剩余 4 * 1024 - 1 個 Segment 作為二級索引,存儲消息的地址。一級索引和二級索引都采用 8byte 存儲每個索引數據。
一級索引 Header
-
一級索引共由 4096 個 8byte 構成。
-
每個索引數據由 64 位存儲,前 32 位為 IP,后 32 位為 baseIndex。
baseIndex = index / 4096,index 為消息遞增序列號。
二級索引
-
二級索引共由 4095 個 segment 構成,每個 segment 由 4096 個 8byte 構成。
-
每個索引數據由 64 位存儲,前 40 位為存儲塊的首地址,后 24 位為解壓后的塊內偏移地址。
一級索引 Header 與二級索引關系
-
一級索引第一個 8byte 存儲可存儲魔數(圖中用 -1 表示),用于標識文件有效性。
-
一級索引剩余 4095 個 8byte 分別與二級索引中每個 segment 順序一一對應。
如何定位一個消息
根據應用名定位對應的索引文件和數據文件。
加載索引文件中的所有一級索引,建立 IP、baseIndex、segmentIndex 的映射表。
從整個索引文件角度看,segmentIndex 是遞增的,1 ~ 4095、4097 ~ 8291,以此類推。
根據消息序列號 index 計算得出 baseIndex。
通過 IP、baseIndex 查找映射表,定位 segmentIndex。
計算消息所對應segment的偏移地址:segmentOffset = (index % 4096) * 8,獲得索引數據。
根據索引數據中塊偏移地址讀取壓縮的數據塊,Snappy 解壓后根據塊內偏移地址讀取消息的二進制數據。
總結
針對類似消息系統的數據存儲,索引設計是比較重要的一環,方案并不是唯一的,需要不斷推敲和完善。文件存儲常用的一些性能優化手段:
-
批量、順序寫,減少磁盤交互次數。
-
4K 對齊寫入。
-
數據壓縮,常用的壓縮算法有 Gzip、Snappy、LZ4。
-
對象池,避免內存頻繁分配。
實踐出真知,推薦大家學習下 Kafka 以及 RocketMQ 源碼,例如 RocketMQ 中單個文件混合存儲的方式、類似 HashMap 結構的 Index 文件設計以及內存映射等都是比較好的學習資源。
總結
以上是生活随笔為你收集整理的深入剖析分布式监控 CAT —— 消息文件存储的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019年DevOps实践最有价值的技能
- 下一篇: 9个用于构建容错系统的开源工具