深度剖析开源分布式监控CAT
CAT(Central Application Tracking)是一個實時和接近全量的監(jiān)控系統(tǒng),它側(cè)重于對Java應(yīng)用的監(jiān)控,基本接入了美團上海側(cè)所有核心應(yīng)用。目前在中間件(MVC、RPC、數(shù)據(jù)庫、緩存等)框架中得到廣泛應(yīng)用,為美團各業(yè)務(wù)線提供系統(tǒng)的性能指標(biāo)、健康狀況、監(jiān)控告警等。
自2014年開源以來,Github 收獲 7700+ Star,2800+ Forks,被 100+ 公司企業(yè)使用,其中不乏攜程、陸金所、獵聘網(wǎng)、平安等業(yè)內(nèi)知 名公司。在每年全球 QCon 大會、全球架構(gòu)與運維技術(shù)峰會等都有持續(xù)的技術(shù)輸出,受到行業(yè)內(nèi)認(rèn)可,越來越多的企業(yè)伙伴加入了 CAT 的開 源建設(shè)工作,為 CAT 的成?貢獻了巨大的力量。
項目的開源地址是 http://github.com/dianping/cat。
本文會對CAT整體設(shè)計、客戶端、服務(wù)端等的一些設(shè)計思路做詳細深入的介紹。
背景介紹
CAT整個產(chǎn)品研發(fā)是從2011年底開始的,當(dāng)時正是大眾點評從.NET遷移到Java的核心起步階段。當(dāng)初大眾點評已經(jīng)有核心的基礎(chǔ)中間件、RPC組件Pigeon、統(tǒng)一配置組件Lion。整體Java遷移已經(jīng)在服務(wù)化的路上。隨著服務(wù)化的深入,整體Java在線上部署規(guī)模逐漸變多,同時,暴露的問題也越來越多。典型的問題有:
- 大量報錯,特別是核心服務(wù),需要花很久時間才能定位。
- 異常日志都需要線上權(quán)限登陸線上機器排查,排錯時間長。
- 有些簡單的錯誤定位都非常困難(一次將線上的庫配置到了Beta,花了整個通宵排錯)。
- 很多不了了之的問題懷疑是網(wǎng)絡(luò)問題(從現(xiàn)在看,內(nèi)網(wǎng)真的很少出問題)。
雖然那時候也有一些簡單的監(jiān)控工具(比如Zabbix,自己研發(fā)的Hawk系統(tǒng)等),可能單個工具在某方面的功能還不錯,但整體服務(wù)化水平參差不齊、擴展能力相對較弱,監(jiān)控工具間不能互通互聯(lián),使得查找問題根源基本都需要在多個系統(tǒng)之間切換,有時候真的是靠“人品”才能找出根源。
適逢在eBay工作長達十幾年的吳其敏加入大眾點評成為首席架構(gòu)師,他對eBay內(nèi)部應(yīng)用非常成功的CAL系統(tǒng)有深刻的理解。就在這樣天時地利人和的情況下,我們開始研發(fā)了大眾點評第一代監(jiān)控系統(tǒng)——CAT。
CAT的原型和理念來源于eBay的CAL系統(tǒng),最初是吳其敏在大眾點評工作期間設(shè)計開發(fā)的。他之前曾CAT不僅增強了CAL系統(tǒng)核心模型,還添加了更豐富的報表。
整體設(shè)計
監(jiān)控整體要求就是快速發(fā)現(xiàn)故障、快速定位故障以及輔助進行程序性能優(yōu)化。為了做到這些,我們對監(jiān)控系統(tǒng)的一些非功能做了如下的要求:
- 實時處理:信息的價值會隨時間銳減,尤其是事故處理過程中。
- 全量數(shù)據(jù):最開始的設(shè)計目標(biāo)就是全量采集,全量的好處有很多。
- 高可用:所有應(yīng)用都倒下了,需要監(jiān)控還站著,并告訴工程師發(fā)生了什么,做到故障還原和問題定位。
- 故障容忍:CAT本身故障不應(yīng)該影響業(yè)務(wù)正常運轉(zhuǎn),CAT掛了,應(yīng)用不該受影響,只是監(jiān)控能力暫時減弱。
- 高吞吐:要想還原真相,需要全方位地監(jiān)控和度量,必須要有超強的處理吞吐能力。
- 可擴展:支持分布式、跨IDC部署,橫向擴展的監(jiān)控系統(tǒng)。
- 不保證可靠:允許消息丟失,這是一個很重要的trade-off,目前CAT服務(wù)端可以做到4個9的可靠性,可靠系統(tǒng)和不可靠性系統(tǒng)的設(shè)計差別非常大。
CAT從開發(fā)至今,一直秉承著簡單的架構(gòu)就是最好的架構(gòu)原則,主要分為三個模塊:CAT-client、CAT-consumer、CAT-home。
- Cat-client 提供給業(yè)務(wù)以及中間層埋點的底層SDK。
- Cat-consumer 用于實時分析從客戶端提供的數(shù)據(jù)。
- Cat-home 作為用戶給用戶提供展示的控制端。
在實際開發(fā)和部署中,Cat-consumer和Cat-home是部署在一個JVM內(nèi)部,每個CAT服務(wù)端都可以作為consumer也可以作為home,這樣既能減少整個層級結(jié)構(gòu),也可以增加系統(tǒng)穩(wěn)定性。
上圖是CAT目前多機房的整體結(jié)構(gòu)圖,圖中可見:
- 路由中心是根據(jù)應(yīng)用所在機房信息來決定客戶端上報的CAT服務(wù)端地址,目前美團有廣州、北京、上海三地機房。
- 每個機房內(nèi)部都有獨立的原始信息存儲集群HDFS。
- CAT-home可以部署在一個機房也可以部署在多個機房,在最后做展示的時候,home會從consumer中進行跨機房的調(diào)用,將所有的數(shù)據(jù)合并展示給用戶。
- 實際過程中,consumer、home以及路由中心都是部署在一起的,每個服務(wù)端節(jié)點都可以充當(dāng)任何一個角色。
客戶端設(shè)計
客戶端設(shè)計是CAT系統(tǒng)設(shè)計中最為核心的一個環(huán)節(jié),客戶端要求是做到API簡單、高可靠性能,無論在任何場景下都不能影響客業(yè)務(wù)性能,監(jiān)控只是公司核心業(yè)務(wù)流程一個旁路環(huán)節(jié)。CAT核心客戶端是Java,也支持Net客戶端,近期公司內(nèi)部也在研發(fā)其他多語言客戶端。以下客戶端設(shè)計及細節(jié)均以Java客戶端為模板。
設(shè)計架構(gòu)
CAT客戶端在收集端數(shù)據(jù)方面使用ThreadLocal(線程局部變量),是線程本地變量,也可以稱之為線程本地存儲。其實ThreadLocal的功用非常簡單,就是為每一個使用該變量的線程都提供一個變量值的副本,屬于Java中一種較為特殊的線程綁定機制,每一個線程都可以獨立地改變自己的副本,不會和其它線程的副本沖突。
在監(jiān)控場景下,為用戶提供服務(wù)都是Web容器,比如tomcat或者Jetty,后端的RPC服務(wù)端比如Dubbo或者Pigeon,也都是基于線程池來實現(xiàn)的。業(yè)務(wù)方在處理業(yè)務(wù)邏輯時基本都是在一個線程內(nèi)部調(diào)用后端服務(wù)、數(shù)據(jù)庫、緩存等,將這些數(shù)據(jù)拿回來再進行業(yè)務(wù)邏輯封裝,最后將結(jié)果展示給用戶。所以將所有的監(jiān)控請求作為一個監(jiān)控上下文存入線程變量就非常合適。
如上圖所示,業(yè)務(wù)執(zhí)行業(yè)務(wù)邏輯的時候,就會把此次請求對應(yīng)的監(jiān)控存放于線程上下文中,存于上下文的其實是一個監(jiān)控樹的結(jié)構(gòu)。在最后業(yè)務(wù)線程執(zhí)行結(jié)束時,將監(jiān)控對象存入一個異步內(nèi)存隊列中,CAT有個消費線程將隊列內(nèi)的數(shù)據(jù)異步發(fā)送到服務(wù)端。
API設(shè)計
監(jiān)控API定義往往取決于對監(jiān)控或者性能分析這個領(lǐng)域的理解,監(jiān)控和性能分析所針對的場景有如下幾種:
- 一段代碼的執(zhí)行時間,一段代碼可以是URL執(zhí)行耗時,也可以是SQL的執(zhí)行耗時。
- 一段代碼的執(zhí)行次數(shù),比如Java拋出異常記錄次數(shù),或者一段邏輯的執(zhí)行次數(shù)。
- 定期執(zhí)行某段代碼,比如定期上報一些核心指標(biāo):JVM內(nèi)存、GC等指標(biāo)。
- 關(guān)鍵的業(yè)務(wù)監(jiān)控指標(biāo),比如監(jiān)控訂單數(shù)、交易額、支付成功率等。
在上述領(lǐng)域模型的基礎(chǔ)上,CAT設(shè)計自己核心的幾個監(jiān)控對象:Transaction、Event、Heartbeat、Metric。
一段監(jiān)控API的代碼示例如下:
序列化和通信
序列化和通信是整個客戶端包括服務(wù)端性能里面很關(guān)鍵的一個環(huán)節(jié)。
- CAT序列化協(xié)議是自定義序列化協(xié)議,自定義序列化協(xié)議相比通用序列化協(xié)議要高效很多,這個在大規(guī)模數(shù)據(jù)實時處理場景下還是非常有必要的。
- CAT通信是基于Netty來實現(xiàn)的NIO的數(shù)據(jù)傳輸,Netty是一個非常好的NIO開發(fā)框架,在這邊就不詳細介紹了。
客戶端埋點
日志埋點是監(jiān)控活動的最重要環(huán)節(jié)之一,日志質(zhì)量決定著監(jiān)控質(zhì)量和效率。當(dāng)前CAT的埋點目標(biāo)是以問題為中心,像程序拋出exception就是典型問題。我個人對問題的定義是:不符合預(yù)期的就可以算問題,比如請求未完成、響應(yīng)時間快了慢了、請求TPS多了少了、時間分布不均勻等等。
在互聯(lián)網(wǎng)環(huán)境中,最突出的問題場景,突出的理解是:跨越邊界的行為。包括但不限于:
- HTTP/REST、RPC/SOA、MQ、Job、Cache、DAL;
- 搜索/查詢引擎、業(yè)務(wù)應(yīng)用、外包系統(tǒng)、遺留系統(tǒng);
- 第三方網(wǎng)關(guān)/銀行, 合作伙伴/供應(yīng)商之間;
- 各類業(yè)務(wù)指標(biāo),如用戶登錄、訂單數(shù)、支付狀態(tài)、銷售額。
遇到的問題
通常Java客戶端在業(yè)務(wù)上使用容易出問題的地方就是內(nèi)存,另外一個是CPU。內(nèi)存往往是內(nèi)存泄露,占用內(nèi)存較多導(dǎo)致業(yè)務(wù)方GC壓力增大; CPU開銷最終就是看代碼的性能。
以前我們遇到過一個極端的例子,我們一個業(yè)務(wù)請求做餐飲加商鋪的銷售額,業(yè)務(wù)一般會通過for循環(huán)所有商鋪的分店,結(jié)果就造成內(nèi)存OOM了,后來發(fā)現(xiàn)這家店是肯德基,有幾萬分店,每個循環(huán)里面都會有數(shù)據(jù)庫連接。在正常場景下,ThreadLocal內(nèi)部的監(jiān)控一個對象就存在幾萬個節(jié)點,導(dǎo)致業(yè)務(wù)Oldgc特別嚴(yán)重。所以說框架的代碼是不能想象業(yè)務(wù)方會怎么用你的代碼,需要考慮到任何情況下都有出問題的可能。
在消耗CPU方面我們也遇到一個case:在某個客戶端版本,CAT本地存儲當(dāng)前消息ID自增的大小,客戶端使用了MappedByteBuffer這個類,這個類是一個文件內(nèi)存映射,測試下來這個類的性能非常高,我們僅僅用這個存儲了幾個字節(jié)的對象,正常情況理論上不會有任何問題。在一次線上場景下,很多業(yè)務(wù)線程都block在這個上面,結(jié)果發(fā)現(xiàn)當(dāng)本身這臺機器IO存在瓶頸時候,這個也會變得很慢。后來的優(yōu)化就是把這個IO的操作異步化,所以客戶端需要盡可能異步化,異步化序列化、異步化傳輸、異步化任何可能存在時間延遲的代碼操作。
服務(wù)端設(shè)計
服務(wù)端主要的問題是大數(shù)據(jù)的實時處理,目前后端CAT的計算集群大約35臺物理機,存儲集群大約35臺物理機,每天處理了約100TB的數(shù)據(jù)量。線上單臺機器高峰期大約是110MB/s,接近千兆網(wǎng)打滿。
下面我重點講下CAT服務(wù)端一些設(shè)計細節(jié)。
架構(gòu)設(shè)計
在最初的整體介紹中已經(jīng)畫了架構(gòu)圖,這邊介紹下單機的consumer中大概的結(jié)構(gòu)如下:
如上圖,CAT服務(wù)端在整個實時處理中,基本上實現(xiàn)了全異步化處理。
- 消息接受是基于Netty的NIO實現(xiàn)。
- 消息接受到服務(wù)端就存放內(nèi)存隊列,然后程序開啟一個線程會消費這個消息做消息分發(fā)。
- 每個消息都會有一批線程并發(fā)消費各自隊列的數(shù)據(jù),以做到消息處理的隔離。
- 消息存儲是先存入本地磁盤,然后異步上傳到HDFS文件,這也避免了強依賴HDFS。
當(dāng)某個報表處理器處理來不及時候,比如Transaction報表處理比較慢,可以通過配置支持開啟多個Transaction處理線程,并發(fā)消費消息。
實時分析
CAT服務(wù)端實時報表分析是整個監(jiān)控系統(tǒng)的核心,CAT重客戶端采集的是是原始的logview,目前一天大約有1000億的消息,這些原始的消息太多了,所以需要在這些消息基礎(chǔ)上實現(xiàn)豐富報表,來支持業(yè)務(wù)問題及性能分析的需要。
CAT是根據(jù)日志消息的特點(比如只讀特性)和問題場景,量身定做的,它將所有的報表按消息的創(chuàng)建時間,一小時為單位分片,那么每小時就產(chǎn)生一個報表。當(dāng)前小時報表的所有計算都是基于內(nèi)存的,用戶每次請求即時報表得到的都是最新的實時結(jié)果。對于歷史報表,因為它是不變的,所以實時不實時也就無所謂了。
CAT基本上所有的報表模型都可以增量計算,它可以分為:計數(shù)、計時和關(guān)系處理三種。計數(shù)又可以分為兩類:算術(shù)計數(shù)和集合計數(shù)。典型的算術(shù)計數(shù)如:總個數(shù)(count)、總和(sum)、均值(avg)、最大/最小(max/min)、吞吐(tps)和標(biāo)準(zhǔn)差(std)等,其他都比較直觀,標(biāo)準(zhǔn)差稍微復(fù)雜一點,大家自己可以推演一下怎么做增量計算。那集合運算,比如95線(表示95%請求的完成時間)、999線(表示99.9%請求的完成時間),則稍微復(fù)雜一些,系統(tǒng)開銷也更大一點。
報表建模
CAT每個報表往往有多個維度,以transaction報表為例,它有5個維度,分別是應(yīng)用、機器、Type、Name和分鐘級分布情況。如果全維度建模,雖然靈活,但開銷將會非常之大。CAT選擇固定維度建模,可以理解成將這5個維度組織成深度為5的樹,訪問時總是從根開始,逐層往下進行。
CAT服務(wù)端為每個報表單獨分配一個線程,所以不會有鎖的問題,所有報表模型都是非線程安全的,其數(shù)據(jù)是可變的。這樣帶來的好處是簡單且低開銷。
CAT報表建模是使用自研的Maven Plugin自動生成的。所有報表是可合并和裁剪的,可以輕易地將2個或多個報表合并成一個報表。在報表處理代碼中,CAT大量使用訪問者模式(visitor pattern)。
性能分析報表
故障發(fā)現(xiàn)報表
- 實時業(yè)務(wù)指標(biāo)監(jiān)控 :核心業(yè)務(wù)都會定義自己的業(yè)務(wù)指標(biāo),這不需要太多,主要用于24小時值班監(jiān)控,實時發(fā)現(xiàn)業(yè)務(wù)指標(biāo)問題,圖中一個是當(dāng)前的實際值,一個是基準(zhǔn)值,就是根據(jù)歷史趨勢計算的預(yù)測值。如下圖就是當(dāng)時的情景,能直觀看到支付業(yè)務(wù)出問題的故障。
- 系統(tǒng)報錯大盤。
- 實時數(shù)據(jù)庫大盤、服務(wù)大盤、緩存大盤等。
存儲設(shè)計
CAT系統(tǒng)的存儲主要有兩塊:
- CAT的報表的存儲。
- CAT原始logview的存儲。
報表是根據(jù)logview實時運算出來的給業(yè)務(wù)分析用的報表,默認(rèn)報表有小時模式、天模式、周模式以及月模式。CAT實時處理報表都是產(chǎn)生小時級別統(tǒng)計,小時級報表中會帶有最低分鐘級別粒度的統(tǒng)計。天、周、月等報表都是在小時級別報表合并的結(jié)果報表。
原始logview存儲一天大約100TB的數(shù)據(jù)量,因為數(shù)據(jù)量比較大所以存儲必須要要壓縮,本身原始logview需要根據(jù)Message-ID讀取,所以存儲整體要求就是批量壓縮以及隨機讀。在當(dāng)時場景下,并沒有特別合適成熟的系統(tǒng)以支持這樣的特性,所以我們開發(fā)了一種基于文件的存儲以支持CAT的場景,在存儲上一直是最難的問題,我們一直在這塊持續(xù)的改進和優(yōu)化。
消息ID的設(shè)計
CAT每個消息都有一個唯一的ID,這個ID在客戶端生成,后續(xù)都通過這個ID在進行消息內(nèi)容的查找。典型的RPC消息串起來的問題,比如A調(diào)用B的時候,在A這端生成一個Message-ID,在A調(diào)用B的過程中,將Message-ID作為調(diào)用傳遞到B端,在B執(zhí)行過程中,B用context傳遞的Message-ID作為當(dāng)前監(jiān)控消息的Message-ID。
CAT消息的Message-ID格式ShopWeb-0a010680-375030-2,CAT消息一共分為四段:
- 第一段是應(yīng)用名shop-web。
- 第二段是當(dāng)前這臺機器的IP的16進制格式,0a01010680表示10.1.6.108。
- 第三段的375030,是系統(tǒng)當(dāng)前時間除以小時得到的整點數(shù)。
- 第四段的2,是表示當(dāng)前這個客戶端在當(dāng)前小時的順序遞增號。
存儲數(shù)據(jù)的設(shè)計
消息存儲是CAT最有挑戰(zhàn)的部分。關(guān)鍵問題是消息數(shù)量多且大,目前美團每天處理消息1000億左右,大小大約100TB,單物理機高峰期每秒要處理100MB左右的流量。CAT服務(wù)端基于此流量做實時計算,還需要將這些數(shù)據(jù)壓縮后寫入磁盤。
整體存儲結(jié)構(gòu)如下圖:
CAT在寫數(shù)據(jù)一份是Index文件,一份是Data文件.
- Data文件是分段GZIP壓縮,每個分段大小小于64K,這樣可以用16bits可以表示一個最大分段地址。
- 一個Message-ID都用需要48bits的大小來存索引,索引根據(jù)Message-ID的第四段來確定索引的位置,比如消息Message-ID為ShopWeb-0a010680-375030-2,這條消息ID對應(yīng)的索引位置為2*48bits的位置。
- 48bits前面32bits存數(shù)據(jù)文件的塊偏移地址,后面16bits存數(shù)據(jù)文件解壓之后的塊內(nèi)地址偏移。
- CAT讀取消息的時候,首先根據(jù)Message-ID的前面三段確定唯一的索引文件,在根據(jù)Message-ID第四段確定此Message-ID索引位置,根據(jù)索引文件的48bits讀取數(shù)據(jù)文件的內(nèi)容,然后將數(shù)據(jù)文件進行GZIP解壓,在根據(jù)塊內(nèi)偏移地址讀取出真正的消息內(nèi)容。
服務(wù)端設(shè)計總結(jié)
CAT在分布式實時方面,主要歸結(jié)于以下幾點因素:
- 去中心化,數(shù)據(jù)分區(qū)處理。
- 基于日志只讀特性,以一個小時為時間窗口,實時報表基于內(nèi)存建模和分析,歷史報表通過聚合完成。
- 基于內(nèi)存隊列,全面異步化、單線程化、無鎖設(shè)計。
- 全局消息ID,數(shù)據(jù)本地化生產(chǎn),集中式存儲。
- 組件化、服務(wù)化理念。
總結(jié)
最后我們再花一點點時間來講一下我們在實踐里做的一些東西。
一、MVP版本,Demo版本用了1個月,MVP版本用了3個月。
為什么強調(diào)MVP版本?因為做這個項目需要老板和業(yè)務(wù)的支持。大概在2011年左右,我們整個生產(chǎn)環(huán)境估計也有一千臺機器(虛擬機),一旦出現(xiàn)問題就到運維那邊看日志,看日志的痛苦大家都應(yīng)該理解,這時候發(fā)現(xiàn)一臺機器核心服務(wù)出錯,可能會導(dǎo)致更多的問題。我們就做了MVP版本解決這個問題,當(dāng)時我們大概做了兩個功能:一個是實時知道所有的API接口訪問量成功率等;第二是實時能在CAT平臺上看到異常日志。這里我想說的是MVP版本不要做太多內(nèi)容,但是在做一個產(chǎn)品的時候必須從MVP版本做起,要做一些最典型特別亮眼的功能讓大家支持你。
二、數(shù)據(jù)質(zhì)量。數(shù)據(jù)質(zhì)量是整個監(jiān)控體系里面非常關(guān)鍵,它決定你最后的監(jiān)控報表質(zhì)量。所以我們要和跟數(shù)據(jù)庫框架、緩存框架、RPC框架、Web框架等做深入的集成,讓業(yè)務(wù)方便收集以及看到這些數(shù)據(jù)。
三、單機開發(fā)環(huán)境,這也是我們認(rèn)為對整個項目開發(fā)效率提升最重要的一點。單機開發(fā)環(huán)境實際上就是說你在一臺機器里可以把你所有的項目都啟起來。如果你在一個單機環(huán)境下把所有東西啟動起來,你就會想方設(shè)法地知道我依賴的服務(wù)掛了我怎么辦?比如CAT依賴了HDFS。單機開發(fā)環(huán)境除了大幅度提高你的項目開發(fā)效率之外,還能提升你整個項目的可靠性。
四、最難的事情是項目上線推動。CAT整個項目大概有兩三個人,當(dāng)時白天都是支持業(yè)務(wù)上線,培訓(xùn),晚上才能code,但是一旦隨著產(chǎn)品和完善以及業(yè)務(wù)使用逐漸變多,一些好的產(chǎn)品后面會形成良性循環(huán),推廣就會變得比較容易。
五、開放生態(tài)。公司越大監(jiān)控的需求越多,報表需求也更多,比如我們美團,產(chǎn)品有很多報表,整個技術(shù)體系里面也有很多報表非常多的自定義報表,很多業(yè)務(wù)方都提各自的需求。最后我們決定把整個CAT系統(tǒng)里面所有的數(shù)據(jù)都作為API暴露出去,這些需求并不是不能支持,而是這事情根本是做不完的。美團內(nèi)部下游有很多系統(tǒng)依賴CAT的數(shù)據(jù),來做進一步的報表展示。
CAT項目從2011年開始做,到現(xiàn)在整個生產(chǎn)環(huán)境大概有三千應(yīng)用,監(jiān)控的服務(wù)端從零到幾千,再到今天的兩萬多的規(guī)模,整個項目是從歷時看起來是一個五年多的項目,但即使是做了五年多的這樣一個項目,目前還有很多的需求需要開發(fā)。這邊也打個廣告,我們團隊急缺人,歡迎對監(jiān)控系統(tǒng)研發(fā)有興趣的同學(xué)加入,請聯(lián)系yong.you@dianping.com.
總結(jié)
以上是生活随笔為你收集整理的深度剖析开源分布式监控CAT的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出排序学习:写给程序员的算法系统开
- 下一篇: 深度剖析RPC框架的核心设计