cat 分散式框架
一、CAT介紹
CAT系統原型和理念來源於eBay的CAL的系統,CAT系統第一代設計者吳其敏在eBay工作長達十幾年,對CAL系統有深刻的理解。CAT不僅增強了CAL系統核心模型,還添加了更豐富的報表。自2014年開源以來,CAT在攜程、陸金所、獵聘網、找鋼網等多家網際網路公司生產環境應用。
CAT是一個實時和接近全量的監控系統,它側重於對Java應用的監控,基本接入了美團點評上海側所有核心應用。目前在中介軟體(MVC、RPC、資料庫、快取等)框架中得到廣泛應用,為美團點評各業務線提供系統的效能指標、健康狀況、監控告警等。
本文會對CAT整體設計、客戶端、服務端等的一些設計思路做詳細深入的介紹。
二、背景介紹
CAT整個產品研發是從2011年底開始的,當時正是大眾點評App Net遷移Java的核心起步階段。當初大眾點評App已經有核心的基礎中介軟體、RPC元件Pigeon、統一配置元件lion。整體Java遷移已經在服務化的路上。隨著服務化的深入,整體Java在線上部署規模逐漸變多,同時,暴露的問題也越來越多。典型的問題有:
-
大量報錯,特別是核心服務,需要花很久時間才能定位。
-
異常日誌都需要線上許可權登陸線上機器排查,排錯時間長。
-
有些簡單的錯誤定位都非常困難(一次將線上的庫配置到了Beta,花了整個通宵排錯)。
-
很多不了了之的問題懷疑是網路問題(從現在看,內網真的很少出問題)。
雖然那時候也有一些簡單的監控工具(比如Zabbix,自己研發的Hawk系統等),可能單個工具在某方面的功能還不錯,但整體服務化水平參差不齊、擴充套件能力相對較弱,監控工具間不能互通互聯,使得查詢問題根源基本都需要在多個系統之間切換,有時候真的是靠“人品”才能找出根源。適逢吳其敏從eBay加入大眾點評成為首席架構師,eBay的CAL系統在內部非常成功,就在這樣天時地利與人和的情況下,我們開始研發了大眾點評App第一代監控系統——CAT。
三、整體設計
監控整體要求就是快速發現故障、快速定位故障以及輔助進行程式效能優化。為了做到這些,我們對監控系統的一些非功能做了如下的要求:
-
實時處理:資訊的價值會隨時間銳減,尤其是事故處理過程中。
-
全量資料:最開始的設計目標就是全量採集,全量的好處有很多。
-
高可用:所有應用都倒下了,需要監控還站著,並告訴工程師發生了什麼,做到故障還原和問題定位。
-
故障容忍: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客戶端為模板。
1、設計架構
CAT客戶端在收集端資料方面使用ThreadLocal(執行緒區域性變數),是執行緒本地變數,也可以稱之為執行緒本地儲存。其實ThreadLocal的功用非常簡單,就是為每一個使用該變數的執行緒都提供一個變數值的副本,屬於Java中一種較為特殊的執行緒繫結機制,每一個執行緒都可以獨立地改變自己的副本,不會和其它執行緒的副本衝突。
在監控場景下,為使用者提供服務都是Web容器,比如TomCAT或者Jetty,後端的RPC服務端比如Dubbo或者Pigeon,也都是基於執行緒池來實現的。業務方在處理業務邏輯時基本都是在一個執行緒內部呼叫後端服務、資料庫、快取等,將這些資料拿回來再進行業務邏輯封裝,最後將結果展示給使用者。所以將所有的監控請求作為一個監控上下文存入執行緒變數就非常合適。
如上圖所示,業務執行業務邏輯的時候,就會把此次請求對應的監控存放於執行緒上下文中,存於上下文的其實是一個監控樹的結構。在最後業務執行緒執行結束時,將監控物件存入一個非同步記憶體佇列中,CAT有個消費執行緒將佇列內的資料非同步傳送到服務端。
2、API設計
監控API定義往往取決於對監控或者效能分析這個領域的理解,監控和效能分析所針對的場景有如下幾種:
-
一段程式碼的執行時間,一段程式碼可以是URL執行耗時,也可以是SQL的執行耗時。
-
一段程式碼的執行次數,比如Java丟擲異常記錄次數,或者一段邏輯的執行次數。
-
定期執行某段程式碼,比如定期上報一些核心指標:JVM記憶體、GC等指標。
-
關鍵的業務監控指標,比如監控訂單數、交易額、支付成功率等。
在上述領域模型的基礎上,CAT設計自己核心的幾個監控物件:Transaction、Event、Heartbeat、Metric。
一段監控API的程式碼示例如下:
3、序列化和通訊
序列化和通訊是整個客戶端包括服務端效能裡面很關鍵的一個環節。
-
CAT序列化協議是自定義序列化協議,自定義序列化協議相比通用序列化協議要高效很多,這個在大規模資料實時處理場景下還是非常有必要的。
-
CAT通訊是基於Netty來實現的NIO的資料傳輸,Netty是一個非常好的NIO開發框架,在這邊就不詳細介紹了。
4、客戶端埋點
日誌埋點是監控活動的最重要環節之一,日誌質量決定著監控質量和效率。當前CAT的埋點目標是以問題為中心,像程式丟擲exception就是典型問題。我個人對問題的定義是:不符合預期的就可以算問題,比如請求未完成、響應時間快了慢了、請求TPS多了少了、時間分佈不均勻等等。
在網際網路環境中,最突出的問題場景,突出的理解是:跨越邊界的行為。包括但不限於:
-
HTTP/REST、RPC/SOA、MQ、Job、Cache、DAL;
-
搜尋/查詢引擎、業務應用、外包系統、遺留系統;
-
第三方閘道器/銀行, 合作伙伴/供應商之間;
-
各類業務指標,如使用者登入、訂單數、支付狀態、銷售額。
5、遇到的問題
通常Java客戶端在業務上使用容易出問題的地方就是記憶體,另外一個是CPU。記憶體往往是記憶體洩露,佔用記憶體較多導致業務方GC壓力增大; CPU開銷最終就是看程式碼的效能。
以前我們遇到過一個極端的例子,我們一個業務請求做餐飲加商鋪的銷售額,業務一般會通過for迴圈所有商鋪的分店,結果就造成記憶體OOM了,後來發現這家店是肯德基,有幾萬分店,每個迴圈裡面都會有資料庫連線。在正常場景下,ThreadLocal內部的監控一個物件就存在幾萬個節點,導致業務Oldgc特別嚴重。所以說框架的程式碼是不能想象業務方會怎麼用你的程式碼,需要考慮到任何情況下都有出問題的可能。
在消耗CPU方面我們也遇到一個case:在某個客戶端版本,CAT本地儲存當前訊息ID自增的大小,客戶端使用了MappedByteBuffer這個類,這個類是一個檔案記憶體對映,測試下來這個類的效能非常高,我們僅僅用這個儲存了幾個位元組的物件,正常情況理論上不會有任何問題。在一次線上場景下,很多業務執行緒都block在這個上面,結果發現當本身這臺機器IO存在瓶頸時候,這個也會變得很慢。後來的優化就是把這個IO的操作非同步化,所以 客戶端需要儘可能非同步化,非同步化序列化、非同步化傳輸、非同步化任何可能存在時間延遲的程式碼操作 。
五、服務端設計
服務端主要的問題是大資料的實時處理,目前後端CAT的計算叢集大約35臺物理機,儲存叢集大約35臺物理機,每天處理了約100TB的資料量。線上單臺機器高峰期大約是110MB/s,接近千兆網打滿。
下面我重點講下CAT服務端一些設計細節。
1、架構設計
在最初的整體介紹中已經畫了架構圖,這邊介紹下單機的consumer中大概的結構如下:
如上圖,CAT服務端在整個實時處理中,基本上實現了全非同步化處理。
-
訊息接受是基於Netty的NIO實現。
-
訊息接受到服務端就存放記憶體佇列,然後程式開啟一個執行緒會消費這個訊息做訊息分發。
-
每個訊息都會有一批執行緒併發消費各自佇列的資料,以做到訊息處理的隔離。
-
訊息儲存是先存入本地磁碟,然後非同步上傳到HDFS檔案,這也避免了強依賴HDFS。
當某個報表處理器處理來不及時候,比如Transaction報表處理比較慢,可以通過配置支援開啟多個Transaction處理執行緒,併發消費訊息。
2、實時分析
CAT服務端實時報表分析是整個監控系統的核心,CAT重客戶端採集的是是原始的logview,目前一天大約有1000億的訊息,這些原始的訊息太多了,所以需要在這些訊息基礎上實現豐富報表,來支援業務問題及效能分析的需要。
CAT是根據日誌訊息的特點(比如只讀特性)和問題場景,量身定做的,它將所有的報表按訊息的建立時間,一小時為單位分片,那麼每小時就產生一個報表。當前小時報表的所有計算都是基於記憶體的,使用者每次請求即時報表得到的都是最新的實時結果。對於歷史報表,因為它是不變的,所以實時不實時也就無所謂了。
CAT基本上所有的報表模型都可以增量計算,它可以分為:計數、計時和關係處理三種。計數又可以分為兩類:算術計數和集合計數。典型的算術計數如:總個數(count)、總和(sum)、均值(avg)、最大/最小(max/min)、吞吐(tps)和標準差(std)等,其他都比較直觀,標準差稍微複雜一點,大家自己可以推演一下怎麼做增量計算。那集合運算,比如95線(表示95%請求的完成時間)、999線(表示99.9%請求的完成時間),則稍微複雜一些,系統開銷也更大一點。
3、報表建模
CAT每個報表往往有多個維度,以transaction報表為例,它有5個維度,分別是應用、機器、Type、Name和分鐘級分佈情況。如果全維度建模,雖然靈活,但開銷將會非常之大。CAT選擇固定維度建模,可以理解成將這5個維度組織成深度為5的樹,訪問時總是從根開始,逐層往下進行。
CAT服務端為每個報表單獨分配一個執行緒,所以不會有鎖的問題,所有報表模型都是非執行緒安全的,其資料是可變的。這樣帶來的好處是簡單且低開銷。
CAT報表建模是使用自研的Maven Plugin自動生成的。所有報表是可合併和裁剪的,可以輕易地將2個或多個報表合併成一個報表。在報表處理程式碼中,CAT大量使用訪問者模式(visitor pattern)。
4、效能分析報表
5、故障發現報表
-
實時業務指標監控 :核心業務都會定義自己的業務指標,這不需要太多,主要用於24小時值班監控,實時發現業務指標問題,圖中一個是當前的實際值,一個是基準值,就是根據歷史趨勢計算的預測值。如下圖就是當時的情景,能直觀看到支付業務出問題的故障。
-
系統報錯大盤。
-
實時資料庫大盤、服務大盤、快取大盤等。
6、儲存設計
CAT系統的儲存主要有兩塊:
-
CAT的報表的儲存。
-
CAT原始logview的儲存。
報表是根據logview實時運算出來的給業務分析用的報表,預設報表有小時模式、天模式、周模式以及月模式。CAT實時處理報表都是產生小時級別統計,小時級報表中會帶有最低分鐘級別粒度的統計。天、周、月等報表都是在小時級別報表合併的結果報表。
原始logview儲存一天大約100TB的資料量,因為資料量比較大所以儲存必須要要壓縮,本身原始logview需要根據Message-ID讀取,所以儲存整體要求就是批量壓縮以及隨機讀。在當時場景下,並沒有特別合適成熟的系統以支援這樣的特性,所以我們開發了一種基於檔案的儲存以支援CAT的場景,在儲存上一直是最難的問題,我們一直在這塊持續的改進和優化。
7、訊息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進位制格式,01010680表示10.1.6.108。
-
第三段的375030,是系統當前時間除以小時得到的整點數。
-
第四段的2,是表示當前這個客戶端在當前小時的順序遞增號。
8、儲存資料的設計
訊息儲存是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解壓,在根據塊內便宜地址讀取出真正的訊息內容。
9、服務端設計總結
CAT在分散式實時方面,主要歸結於以下幾點因素:
-
去中心化,資料分割槽處理。
-
基於日誌只讀特性,以一個小時為時間視窗,實時報表基於記憶體建模和分析,歷史報表通過聚合完成。
-
基於記憶體佇列,全面非同步化、單執行緒化、無鎖設計。
-
全域性訊息ID,資料本地化生產,集中式儲存。
-
元件化、服務化理念。
五、總結感悟
最後我們再花一點點時間來講一下我們在實踐裡做的一些東西。
一、MVP版本,Demo版本用了1個月,MVP版本用了3個月。
為什麼強調MVP版本?因為做這個專案需要老闆和業務的支援。大概在2011年左右,我們整個生產環境估計也有一千臺機器(虛擬機器),一旦出現問題就到運維那邊看日誌,看日誌的痛苦大家都應該理解,這時候發現一臺機器核心服務出錯,可能會導致更多的問題。我們就做了MVP版本解決這個問題,當時我們大概做了兩個功能:一個是實時知道所有的API介面訪問量成功率等;第二是實時能在CAT平臺上看到異常日誌。這裡我想說的是MVP版本不要做太多內容,但是在做一個產品的時候必須從MVP版本做起,要做一些最典型特別亮眼的功能讓大家支援你。
二、資料質量。資料質量是整個監控體系裡面非常關鍵,它決定你最後的監控報表質量。所以我們要和跟資料庫框架、快取框架、RPC框架、Web框架等做深入的整合,讓業務方便收集以及看到這些資料。
三、單機開發環境,這也是我們認為對整個專案開發效率提升最重要的一點。單機開發環境實際上就是說你在一臺機器裡可以把你所有的專案都啟起來。如果你在一個單機環境下把所有東西啟動起來,你就會想方設法地知道我依賴的服務掛了我怎麼辦?比如CAT依賴了HDFS。單機開發環境除了大幅度提高你的專案開發效率之外,還能提升你整個專案的可靠性。
四、最難的事情是專案上線推動。CAT整個專案大概有兩三個人,當時白天都是支援業務上線,培訓,晚上才能code,但是一旦隨著產品和完善以及業務使用逐漸變多,一些好的產品後面會形成良性迴圈,推廣就會變得比較容易。
五、開放生態。公司越大監控的需求越多,報表需求也更多,比如我們美團點評,產品有很多報表,整個技術體系裡面也有很多報表非常多的自定義報表,很多業務方都提各自的需求。最後我們決定把整個CAT系統裡面所有的資料都作為API暴露出去,這些需求並不是不能支援,而是這事情根本是做不完的。美團點評內部下游有很多系統依賴CAT的資料,來做進一步的報表展示。