CAT(Central Application Tracking)是一個實時和接近全量的監控系統,它側重於對Java應用的監控,基本接入了美團上海側全部核心應用。目前在中間件(MVC、RPC、數據庫、緩存等)框架中獲得普遍應用,爲美團各業務線提供系統的性能指標、健康情況、監控告警等。

自2014年開源以來,Github 收穫 7700+ Star,2800+ Forks,被 100+ 公司企業使用,其中不乏攜程、陸金所、獵聘網、平安等業內知 名公司。在每一年全球 QCon 大會、全球架構與運維技術峯會等都有持續的技術輸出,受到行業內承認,愈來愈多的企業夥伴加入了 CAT 的開 源建設工做,爲 CAT 的成⻓貢獻了巨大的力量。

項目的開源地址是 http://github.com/dianping/cat

本文會對CAT總體設計、客戶端、服務端等的一些設計思路作詳細深刻的介紹。

背景介紹

CAT整個產品研發是從2011年末開始的,當時正是大衆點評從.NET遷移到Java的核心起步階段。當初大衆點評已經有核心的基礎中間件、RPC組件Pigeon、統一配置組件Lion。總體Java遷移已經在服務化的路上。隨着服務化的深刻,總體Java在線上部署規模逐漸變多,同時,暴露的問題也愈來愈多。典型的問題有:

  • 大量報錯,特別是核心服務,須要花好久時間才能定位。
  • 異常日誌都須要線上權限登錄線上機器排查,排錯時間長。
  • 有些簡單的錯誤定位都很是困難(一次將線上的庫配置到了Beta,花了整個通宵排錯)。
  • 不少不了了之的問題懷疑是網絡問題(從如今看,內網真的不多出問題)。

雖然那時候也有一些簡單的監控工具(好比Zabbix,本身研發的Hawk系統等),可能單個工具在某方面的功能還不錯,但總體服務化水平良莠不齊、擴展能力相對較弱,監控工具間不能互通互聯,使得查找問題根源基本都須要在多個系統之間切換,有時候真的是靠「人品」才能找出根源。

適逢在eBay工做長達十幾年的吳其敏加入大衆點評成爲首席架構師,他對eBay內部應用很是成功的CAL系統有深入的理解。就在這樣天時地利人和的狀況下,咱們開始研發了大衆點評第一代監控系統——CAT。

CAT的原型和理念來源於eBay的CAL系統,最初是吳其敏在大衆點評工做期間設計開發的。他以前曾CAT不只加強了CAL系統核心模型,還添加了更豐富的報表。

總體設計

監控總體要求就是快速發現故障、快速定位故障以及輔助進行程序性能優化。爲了作到這些,咱們對監控系統的一些非功能作了以下的要求:

  • 實時處理:信息的價值會隨時間銳減,尤爲是事故處理過程當中。
  • 全量數據:最開始的設計目標就是全量採集,全量的好處有不少。
  • 高可用:全部應用都倒下了,須要監控還站着,並告訴工程師發生了什麼,作到故障還原和問題定位。
  • 故障容忍:CAT自己故障不該該影響業務正常運轉,CAT掛了,應用不應受影響,只是監控能力暫時減弱。
  • 高吞吐:要想還原真相,須要全方位地監控和度量,必需要有超強的處理吞吐能力。
  • 可擴展:支持分佈式、跨IDC部署,橫向擴展的監控系統。
  • 不保證可靠:容許消息丟失,這是一個很重要的trade-off,目前CAT服務端能夠作到4個9的可靠性,可靠系統和不可靠性系統的設計差異很是大。

CAT從開發至今,一直秉承着簡單的架構就是最好的架構原則,主要分爲三個模塊:CAT-client、CAT-consumer、CAT-home。

  • Cat-client 提供給業務以及中間層埋點的底層SDK。
  • Cat-consumer 用於實時分析從客戶端提供的數據。
  • Cat-home 做爲用戶給用戶提供展現的控制端。

在實際開發和部署中,Cat-consumer和Cat-home是部署在一個JVM內部,每一個CAT服務端均可以做爲consumer也能夠做爲home,這樣既能減小整個層級結構,也能夠增長系統穩定性。

上圖是CAT目前多機房的總體結構圖,圖中可見:

  • 路由中心是根據應用所在機房信息來決定客戶端上報的CAT服務端地址,目前美團有廣州、北京、上海三地機房。
  • 每一個機房內部都有獨立的原始信息存儲集羣HDFS。
  • CAT-home能夠部署在一個機房也能夠部署在多個機房,在最後作展現的時候,home會從consumer中進行跨機房的調用,將全部的數據合併展現給用戶。
  • 實際過程當中,consumer、home以及路由中心都是部署在一塊兒的,每一個服務端節點均可以充當任何一個角色。

客戶端設計

客戶端設計是CAT系統設計中最爲核心的一個環節,客戶端要求是作到API簡單、高可靠性能,不管在任何場景下都不能影響客業務性能,監控只是公司核心業務流程一個旁路環節。CAT核心客戶端是Java,也支持Net客戶端,近期公司內部也在研發其餘多語言客戶端。如下客戶端設計及細節均以Java客戶端爲模板。

設計架構

CAT客戶端在收集端數據方面使用ThreadLocal(線程局部變量),是線程本地變量,也能夠稱之爲線程本地存儲。其實ThreadLocal的功用很是簡單,就是爲每個使用該變量的線程都提供一個變量值的副本,屬於Java中一種較爲特殊的線程綁定機制,每個線程均可以獨立地改變本身的副本,不會和其它線程的副本衝突。

在監控場景下,爲用戶提供服務都是Web容器,好比tomcat或者Jetty,後端的RPC服務端好比Dubbo或者Pigeon,也都是基於線程池來實現的。業務方在處理業務邏輯時基本都是在一個線程內部調用後端服務、數據庫、緩存等,將這些數據拿回來再進行業務邏輯封裝,最後將結果展現給用戶。因此將全部的監控請求做爲一個監控上下文存入線程變量就很是合適。

如上圖所示,業務執行業務邏輯的時候,就會把這次請求對應的監控存放於線程上下文中,存於上下文的實際上是一個監控樹的結構。在最後業務線程執行結束時,將監控對象存入一個異步內存隊列中,CAT有個消費線程將隊列內的數據異步發送到服務端。

API設計

監控API定義每每取決於對監控或者性能分析這個領域的理解,監控和性能分析所針對的場景有以下幾種:

  • 一段代碼的執行時間,一段代碼能夠是URL執行耗時,也能夠是SQL的執行耗時。
  • 一段代碼的執行次數,好比Java拋出異常記錄次數,或者一段邏輯的執行次數。
  • 按期執行某段代碼,好比按期上報一些核心指標:JVM內存、GC等指標。
  • 關鍵的業務監控指標,好比監控訂單數、交易額、支付成功率等。

在上述領域模型的基礎上,CAT設計本身核心的幾個監控對象:Transaction、Event、Heartbeat、Metric。

一段監控API的代碼示例以下:

序列化和通訊

序列化和通訊是整個客戶端包括服務端性能裏面很關鍵的一個環節。

  • CAT序列化協議是自定義序列化協議,自定義序列化協議相比通用序列化協議要高效不少,這個在大規模數據實時處理場景下仍是很是有必要的。
  • CAT通訊是基於Netty來實現的NIO的數據傳輸,Netty是一個很是好的NIO開發框架,在這邊就不詳細介紹了。

客戶端埋點

日誌埋點是監控活動的最重要環節之一,日誌質量決定着監控質量和效率。當前CAT的埋點目標是以問題爲中心,像程序拋出exception就是典型問題。我我的對問題的定義是:不符合預期的就能夠算問題,好比請求未完成、響應時間快了慢了、請求TPS多了少了、時間分佈不均勻等等。

在互聯網環境中,最突出的問題場景,突出的理解是:跨越邊界的行爲。包括但不限於:

  • HTTP/REST、RPC/SOA、MQ、Job、Cache、DAL;
  • 搜索/查詢引擎、業務應用、外包系統、遺留系統;
  • 第三方網關/銀行, 合做夥伴/供應商之間;
  • 各種業務指標,如用戶登陸、訂單數、支付狀態、銷售額。

遇到的問題

一般Java客戶端在業務上使用容易出問題的地方就是內存,另一個是CPU。內存每每是內存泄露,佔用內存較多致使業務方GC壓力增大; CPU開銷最終就是看代碼的性能。

之前咱們遇到過一個極端的例子,咱們一個業務請求作餐飲加商鋪的銷售額,業務通常會經過for循環全部商鋪的分店,結果就形成內存OOM了,後來發現這家店是肯德基,有幾萬分店,每一個循環裏面都會有數據庫鏈接。在正常場景下,ThreadLocal內部的監控一個對象就存在幾萬個節點,致使業務Oldgc特別嚴重。因此說框架的代碼是不能想象業務方會怎麼用你的代碼,須要考慮到任何狀況下都有出問題的可能。

在消耗CPU方面咱們也遇到一個case:在某個客戶端版本,CAT本地存儲當前消息ID自增的大小,客戶端使用了MappedByteBuffer這個類,這個類是一個文件內存映射,測試下來這個類的性能很是高,咱們僅僅用這個存儲了幾個字節的對象,正常狀況理論上不會有任何問題。在一次線上場景下,不少業務線程都block在這個上面,結果發現當自己這臺機器IO存在瓶頸時候,這個也會變得很慢。後來的優化就是把這個IO的操做異步化,因此客戶端須要儘量異步化,異步化序列化、異步化傳輸、異步化任何可能存在時間延遲的代碼操做。

服務端設計

服務端主要的問題是大數據的實時處理,目先後端CAT的計算集羣大約35臺物理機,存儲集羣大約35臺物理機,天天處理了約100TB的數據量。線上單臺機器高峯期大約是110MB/s,接近千兆網打滿。

下面我重點講下CAT服務端一些設計細節。

架構設計

在最初的總體介紹中已經畫了架構圖,這邊介紹下單機的consumer中大概的結構以下:

如上圖,CAT服務端在整個實時處理中,基本上實現了全異步化處理。

  • 消息接受是基於Netty的NIO實現。
  • 消息接受到服務端就存放內存隊列,而後程序開啓一個線程會消費這個消息作消息分發。
  • 每一個消息都會有一批線程併發消費各自隊列的數據,以作到消息處理的隔離。
  • 消息存儲是先存入本地磁盤,而後異步上傳到HDFS文件,這也避免了強依賴HDFS。

當某個報表處理器處理來不及時候,好比Transaction報表處理比較慢,能夠經過配置支持開啓多個Transaction處理線程,併發消費消息。

實時分析

CAT服務端實時報表分析是整個監控系統的核心,CAT重客戶端採集的是是原始的logview,目前一天大約有1000億的消息,這些原始的消息太多了,因此須要在這些消息基礎上實現豐富報表,來支持業務問題及性能分析的須要。

CAT是根據日誌消息的特色(好比只讀特性)和問題場景,量身定作的,它將全部的報表按消息的建立時間,一小時爲單位分片,那麼每小時就產生一個報表。當前小時報表的全部計算都是基於內存的,用戶每次請求即時報表獲得的都是最新的實時結果。對於歷史報表,由於它是不變的,因此實時不實時也就無所謂了。

CAT基本上全部的報表模型均可以增量計算,它能夠分爲:計數、計時和關係處理三種。計數又能夠分爲兩類:算術計數和集合計數。典型的算術計數如:總個數(count)、總和(sum)、均值(avg)、最大/最小(max/min)、吞吐(tps)和標準差(std)等,其餘都比較直觀,標準差稍微複雜一點,你們本身能夠推演一下怎麼作增量計算。那集合運算,好比95線(表示95%請求的完成時間)、999線(表示99.9%請求的完成時間),則稍微複雜一些,系統開銷也更大一點。

報表建模

CAT每一個報表每每有多個維度,以transaction報表爲例,它有5個維度,分別是應用、機器、Type、Name和分鐘級分佈狀況。若是全維度建模,雖然靈活,但開銷將會很是之大。CAT選擇固定維度建模,能夠理解成將這5個維度組織成深度爲5的樹,訪問時老是從根開始,逐層往下進行。

CAT服務端爲每一個報表單獨分配一個線程,因此不會有鎖的問題,全部報表模型都是非線程安全的,其數據是可變的。這樣帶來的好處是簡單且低開銷。

CAT報表建模是使用自研的Maven Plugin自動生成的。全部報表是可合併和裁剪的,能夠輕易地將2個或多個報表合併成一個報表。在報表處理代碼中,CAT大量使用訪問者模式(visitor pattern)。

性能分析報表

故障發現報表

  • 實時業務指標監控 :核心業務都會定義本身的業務指標,這不須要太多,主要用於24小時值班監控,實時發現業務指標問題,圖中一個是當前的實際值,一個是基準值,就是根據歷史趨勢計算的預測值。以下圖就是當時的情景,能直觀看到支付業務出問題的故障。

  • 系統報錯大盤。
  • 實時數據庫大盤、服務大盤、緩存大盤等。

存儲設計

CAT系統的存儲主要有兩塊:

  • CAT的報表的存儲。
  • CAT原始logview的存儲。

報表是根據logview實時運算出來的給業務分析用的報表,默認報表有小時模式、天模式、周模式以及月模式。CAT實時處理報表都是產生小時級別統計,小時級報表中會帶有最低分鐘級別粒度的統計。天、周、月等報表都是在小時級別報表合併的結果報表。

原始logview存儲一天大約100TB的數據量,由於數據量比較大因此存儲必需要要壓縮,自己原始logview須要根據Message-ID讀取,因此存儲總體要求就是批量壓縮以及隨機讀。在當時場景下,並無特別合適成熟的系統以支持這樣的特性,因此咱們開發了一種基於文件的存儲以支持CAT的場景,在存儲上一直是最難的問題,咱們一直在這塊持續的改進和優化。

消息ID的設計

CAT每一個消息都有一個惟一的ID,這個ID在客戶端生成,後續都經過這個ID在進行消息內容的查找。典型的RPC消息串起來的問題,好比A調用B的時候,在A這端生成一個Message-ID,在A調用B的過程當中,將Message-ID做爲調用傳遞到B端,在B執行過程當中,B用context傳遞的Message-ID做爲當前監控消息的Message-ID。

CAT消息的Message-ID格式ShopWeb-0a010680-375030-2,CAT消息一共分爲四段:

  • 第一段是應用名shop-web。
  • 第二段是當前這臺機器的IP的16進制格式,0a01010680表示10.1.6.108。
  • 第三段的375030,是系統當前時間除以小時獲得的整點數。
  • 第四段的2,是表示當前這個客戶端在當前小時的順序遞增號。

存儲數據的設計

消息存儲是CAT最有挑戰的部分。關鍵問題是消息數量多且大,目前美團天天處理消息1000億左右,大小大約100TB,單物理機高峯期每秒要處理100MB左右的流量。CAT服務端基於此流量作實時計算,還須要將這些數據壓縮後寫入磁盤。

總體存儲結構以下圖:

CAT在寫數據一份是Index文件,一份是Data文件.

  • Data文件是分段GZIP壓縮,每一個分段大小小於64K,這樣能夠用16bits能夠表示一個最大分段地址。
  • 一個Message-ID都用須要48bits的大小來存索引,索引根據Message-ID的第四段來肯定索引的位置,好比消息Message-ID爲ShopWeb-0a010680-375030-2,這條消息ID對應的索引位置爲2*48bits的位置。
  • 48bits前面32bits存數據文件的塊偏移地址,後面16bits存數據文件解壓以後的塊內地址偏移。
  • CAT讀取消息的時候,首先根據Message-ID的前面三段肯定惟一的索引文件,在根據Message-ID第四段肯定此Message-ID索引位置,根據索引文件的48bits讀取數據文件的內容,而後將數據文件進行GZIP解壓,在根據塊內偏移地址讀取出真正的消息內容。

服務端設計總結

CAT在分佈式實時方面,主要歸結於如下幾點因素:

  • 去中心化,數據分區處理。
  • 基於日誌只讀特性,以一個小時爲時間窗口,實時報表基於內存建模和分析,歷史報表經過聚合完成。
  • 基於內存隊列,全面異步化、單線程化、無鎖設計。
  • 全局消息ID,數據本地化生產,集中式存儲。
  • 組件化、服務化理念。

總結

最後咱們再花一點點時間來說一下咱們在實踐裏作的一些東西。

1、MVP版本,Demo版本用了1個月,MVP版本用了3個月。

爲何強調MVP版本?由於作這個項目須要老闆和業務的支持。大概在2011年左右,咱們整個生產環境估計也有一千臺機器(虛擬機),一旦出現問題就到運維那邊看日誌,看日誌的痛苦你們都應該理解,這時候發現一臺機器核心服務出錯,可能會致使更多的問題。咱們就作了MVP版本解決這個問題,當時咱們大概作了兩個功能:一個是實時知道全部的API接口訪問量成功率等;第二是實時能在CAT平臺上看到異常日誌。這裏我想說的是MVP版本不要作太多內容,可是在作一個產品的時候必須從MVP版本作起,要作一些最典型特別亮眼的功能讓你們支持你。

2、數據質量。數據質量是整個監控體系裏面很是關鍵,它決定你最後的監控報表質量。因此咱們要和跟數據庫框架、緩存框架、RPC框架、Web框架等作深刻的集成,讓業務方便收集以及看到這些數據。

3、單機開發環境,這也是咱們認爲對整個項目開發效率提高最重要的一點。單機開發環境實際上就是說你在一臺機器裏能夠把你全部的項目都啓起來。若是你在一個單機環境下把全部東西啓動起來,你就會千方百計地知道我依賴的服務掛了我怎麼辦?好比CAT依賴了HDFS。單機開發環境除了大幅度提升你的項目開發效率以外,還能提高你整個項目的可靠性。

4、最難的事情是項目上線推進。CAT整個項目大概有兩三我的,當時白天都是支持業務上線,培訓,晚上才能code,可是一旦隨着產品和完善以及業務使用逐漸變多,一些好的產品後面會造成良性循環,推廣就會變得比較容易。

5、開放生態。公司越大監控的需求越多,報表需求也更多,好比咱們美團,產品有不少報表,整個技術體系裏面也有不少報表很是多的自定義報表,不少業務方都提各自的需求。最後咱們決定把整個CAT系統裏面全部的數據都做爲API暴露出去,這些需求並非不能支持,而是這事情根本是作不完的。美團內部下游有不少系統依賴CAT的數據,來作進一步的報表展現。

CAT項目從2011年開始作,到如今整個生產環境大概有三千應用,監控的服務端從零到幾千,再到今天的兩萬多的規模,整個項目是從歷時看起來是一個五年多的項目,但即便是作了五年多的這樣一個項目,目前還有不少的需求須要開發。這邊也打個廣告,咱們團隊急缺人,歡迎對監控系統研發有興趣的同窗加入,請聯繫yong.you@dianping.com.