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