深度剖析開源分佈式監控CAT

深度剖析開源分佈式監控CAT

CAT(Central Application Tracking)是一個實時和接近全量的監控系統,它側重於對Java應用的監控,基本接入了美團上海側所有核心應用。目前在中間件(MVC、RPC、數據庫、緩存等)框架中得到廣泛應用,為美團各業務線提供系統的性能指標、健康狀況、監控告警等。自2014年開源以來,除了美團之外,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目前多機房的整體結構圖,圖中可見:

  • 路由中心是根據應用所在機房信息來決定客戶端上報的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

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

API設計

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

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

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

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

深度剖析開源分佈式監控CAT

序列化和通信

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

  • 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

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

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

當某個報表處理器處理來不及時候,比如Transaction報表處理比較慢,可以通過配置支持開啟多個Transaction處理線程,併發消費消息。

深度剖析開源分佈式監控CAT

實時分析

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)。

性能分析報表

深度剖析開源分佈式監控CAT

故障發現報表

  • 實時業務指標監控 :核心業務都會定義自己的業務指標,這不需要太多,主要用於24小時值班監控,實時發現業務指標問題,圖中一個是當前的實際值,一個是基準值,就是根據歷史趨勢計算的預測值。如下圖就是當時的情景,能直觀看到支付業務出問題的故障。
深度剖析開源分佈式監控CAT

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

存儲設計

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

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,數據本地化生產,集中式存儲。
  • 組件化、服務化理念。

總結感悟

最後我們再花一點點時間來講一下我們在實踐裡做的一些東西。

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

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

二、數據質量。數據質量是整個監控體系裡面非常關鍵,它決定你最後的監控報表質量。所以我們要和跟數據庫框架、緩存框架、RPC框架、Web框架等做深入的集成,讓業務方便收集以及看到這些數據。

三、單機開發環境,這也是我們認為對整個項目開發效率提升最重要的一點。單機開發環境實際上就是說你在一臺機器裡可以把你所有的項目都啟起來。如果你在一個單機環境下把所有東西啟動起來,你就會想方設法地知道我依賴的服務掛了我怎麼辦?比如CAT依賴了HDFS。單機開發環境除了大幅度提高你的項目開發效率之外,還能提升你整個項目的可靠性。

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

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

CAT項目從2011年開始做,到現在整個生產環境大概有三千應用,監控的服務端從零到幾千,再到今天的兩萬多的規模,整個項目是從歷時看起來是一個五年多的項目,但即使是做了五年多的這樣一個項目,目前還有很多的需求需要開發。


分享到:


相關文章: