CAT(Central Application Tracking),是基於 Java 開發的分佈式實時監控系統。CAT 目前在美團點評的產品定位是應用層的統一監控組件,在中間件(RPC、數據庫、緩存、MQ 等)框架中獲得普遍應用,爲各業務線提供系統的性能指標、健康情況、實時告警等。java
CAT 目前在美團點評已經基本覆蓋所有業務線,天天處理的消息總量 3200 億+,存儲消息量近 400TB,在通訊、計算、存儲方面都遇到了很大的挑戰。git
感興趣的朋友歡迎 Star 開源項目 github.com/dianping/ca…。github
消息類型 | 職責 | 適用場景 |
---|---|---|
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 duration
return null;
}
private void doRpcCall() {
throw new RuntimeException("rpc call timeout"); // mock rpc timeout
}
複製代碼
LogView 不只能夠分析核心流程的性能耗時,並且能夠幫助用戶快速排查和定位問題。
例如上述埋點示例對應的 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 線程構成。因爲原始消息的數量龐大,因此須要對數據進行加工、統計後生成豐富的報表,知足業務方排查問題以及性能分析的需求。api
其中 Logview 的 Analyzer 線程是本文討論的重點,它會收集全量的原始消息,並實時寫入磁盤,相似實現一個高吞吐量的簡易版消息系統。此外須要具有必定限度的隨機讀能力,方便業務方定位問題發生時的「案發現場」
。緩存
對於歷史的 Logview 文件會異步上傳至 HDFS。性能優化
CAT 針對消息寫多讀少的場景,設計並實現了一套文件存儲。以小時爲單位進行集中式存儲,每一個小時對應一個存儲目錄,存儲文件分爲索引文件和數據文件。用戶能夠根據 MessageID 快讀定位到某一個消息。app
CAT 客戶端會爲每一個消息樹都會分配惟一的 MessageID,MessageID 總共分爲四段,示例格式:shop.service-0a010101-431699-1000。框架
存儲設計的重要依據點
)V1.0 版本的文件存儲設計比較簡單粗暴,每一個客戶端 IP 節點對應分別對應一個索引文件和數據文件。
優勢 | 缺點 |
---|---|
1. 簡單易懂,實現複雜度不高。 2. 根據消息序列號可快速定位索引。 |
1. 隨着業務規模不斷擴展,存儲文件的數量並不可控。 2. 數據文件節點過多致使隨機 IO 惡化,機器 Load 飆高。 |
V2.0 文件存儲進行了從新設計,以解決 V2.0 數據文件節點過多以及隨機 IO 惡化的問題。
V2.0 核心設計思想:
索引文件存儲的特色:
上圖是索引結構的最小單元,每一個索引文件由若干個最小單元組成。每一個單元分爲 4 * 1024 個 Segment,第一個 Segment 做爲咱們的一級索引 Header,存儲 IP、消息序列號與 Segment 的映射信息。剩餘 4 * 1024 - 1 個 Segment 做爲二級索引,存儲消息的地址。一級索引和二級索引都採用 8byte 存儲每一個索引數據。
一級索引 Header
一級索引共由 4096 個 8byte 構成。
每一個索引數據由 64 位存儲,前 32 位爲 IP,後 32 位爲 baseIndex。
baseIndex = index / 4096,index 爲消息遞增序列號。
二級索引
一級索引 Header 與二級索引關係
如何定位一個消息
根據應用名定位對應的索引文件和數據文件。
加載索引文件中的全部一級索引,創建 IP、baseIndex、segmentIndex 的映射表。
從整個索引文件角度看,segmentIndex 是遞增的,1 ~ 409五、4097 ~ 8291,以此類推。
根據消息序列號 index 計算得出 baseIndex。
經過 IP、baseIndex 查找映射表,定位 segmentIndex。
計算消息所對應segment的偏移地址:segmentOffset = (index % 4096) * 8,得到索引數據。
根據索引數據中塊偏移地址讀取壓縮的數據塊,Snappy 解壓後根據塊內偏移地址讀取消息的二進制數據。
針對相似消息系統的數據存儲,索引設計是比較重要的一環,方案並非惟一的,須要不斷推敲和完善。文件存儲經常使用的一些性能優化手段:
實踐出真知,推薦你們學習下 Kafka 以及 RocketMQ 源碼,例如 RocketMQ 中單個文件混合存儲的方式、相似 HashMap 結構的 Index 文件設計以及內存映射等都是比較好的學習資源。
轉載請註明出處,歡迎關注個人公衆號:亞普的技術輪子