微信海量數(shù)據(jù)監(jiān)控的設(shè)計與實踐
本文分享的是微信運維監(jiān)控系統(tǒng)的具體設(shè)計實踐。在分享開始之前,我們先看如下圖中微信后臺系統(tǒng)的現(xiàn)狀。
面對龐大的調(diào)用量及復(fù)雜的調(diào)用鏈路,單靠人力難以維護(hù),只能依賴一個全方位監(jiān)控、穩(wěn)定、快速的運維監(jiān)控系統(tǒng)。
我們的運維監(jiān)控系統(tǒng)主要有三個功能:
- 故障報警
- 故障分析和定位
- 自動化策略
今天我們的分享主題,主要有以下三部分:
- 監(jiān)控數(shù)據(jù)收集輕量化
- 微信數(shù)據(jù)監(jiān)控的發(fā)展過程
- 海量監(jiān)控分析下的數(shù)據(jù)存儲設(shè)計思路
監(jiān)控數(shù)據(jù)收集輕量化
先看一下常見的數(shù)據(jù)收集流程,一般從日志里面采集,然后本地匯總打包,再發(fā)到全局服務(wù)器里面匯總。
但是對于微信來說,200w/min 調(diào)用量產(chǎn)生的是 2000億/min 的監(jiān)控數(shù)據(jù)上報,這個還是比較保守的估計數(shù)據(jù)。
早期我們使用過自定義文本類型日志上報,但由于業(yè)務(wù)及后臺服務(wù)非常多,日志格式增長非???,難以持續(xù)進(jìn)行維護(hù),而且不管是 CPU、網(wǎng)絡(luò)、存儲、統(tǒng)計都出現(xiàn)非常大的壓力,難以保證監(jiān)控系統(tǒng)本身的穩(wěn)定。
為了實現(xiàn)穩(wěn)定的分鐘級、甚至秒級的數(shù)據(jù)監(jiān)控,我們進(jìn)行了一系列改造。
對于我們內(nèi)部監(jiān)控數(shù)據(jù)處理分為兩個步驟:
- 數(shù)據(jù)分類
- 定制處理策略
我們對數(shù)據(jù)進(jìn)行分類,在我們內(nèi)部來說有三種數(shù)據(jù):
- 實時故障監(jiān)控分析。
- 非實時數(shù)據(jù)統(tǒng)計,比如說業(yè)務(wù)報表等。
- 單用戶異常分析,比如說用戶一個報障過來還要單獨對用戶故障進(jìn)行分析。
下面先簡單介紹一下非實時數(shù)據(jù)統(tǒng)計及單用戶異常分析,再重點介紹實時監(jiān)控數(shù)據(jù)的處理。
非實時數(shù)據(jù)
對于非實時數(shù)據(jù)來說,我們有一個配置管理頁面。
用戶在上報的時候會先申請 logid + 自定義數(shù)據(jù)字段,上報并非使用寫日志文件的方式,而是采用共享內(nèi)存隊列、批量打包發(fā)送的方式減少磁盤 IO、日志服務(wù)器的調(diào)用壓力。統(tǒng)計使用分布式統(tǒng)計,目前已經(jīng)是常規(guī)做法。
單用戶異常分析
對于單個用戶異常分析來說,我們關(guān)注的是異常,所以上報路徑跟剛才非實時的路徑比較相近。
采用固定的格式: logid + 固定數(shù)據(jù)字段(服務(wù)器 IP+返回碼等)。
數(shù)據(jù)上報量比剛才的非實時日志還要大很多,所以我們是抽樣上報的,除了把數(shù)據(jù)存入到 Tdw 分布式存儲里面,還會把它轉(zhuǎn)發(fā)到另外一個緩存里面進(jìn)行一個查詢緩存。
實時監(jiān)控數(shù)據(jù)
實時監(jiān)控數(shù)據(jù)是重點分享的部分,這部分?jǐn)?shù)據(jù)也是 2000億/min 日志上報中的絕大多數(shù)。
為了實現(xiàn)分方位的監(jiān)控,我們的實時監(jiān)控數(shù)據(jù)也有很多種類型,其格式、來源、統(tǒng)計方式都有差異。
為了實現(xiàn)快速穩(wěn)定的數(shù)據(jù)監(jiān)控,我們對數(shù)據(jù)進(jìn)行了分類,然后針對性的對各類數(shù)據(jù)進(jìn)行簡化、統(tǒng)一數(shù)據(jù)格式,再對簡化后的數(shù)據(jù)采取***的數(shù)據(jù)處理策略。
對我們數(shù)據(jù)來說,我們覺得有下面幾種:
- 后臺數(shù)據(jù)監(jiān)控,用于微信后臺服務(wù)的監(jiān)控數(shù)據(jù)。
- 終端數(shù)據(jù)監(jiān)控,除了后臺,我們還需要關(guān)注終端方面具體的性能、異常監(jiān)控及網(wǎng)絡(luò)異常。
- 對外監(jiān)控服務(wù),我們現(xiàn)在有商戶和小程序等外部開發(fā)者提供的服務(wù),我們及外部服務(wù)開發(fā)者都需要知道這個服務(wù)和我們微信之間有些怎么樣的異常,所以我們還提供了對外的監(jiān)控服務(wù)。
后臺數(shù)據(jù)監(jiān)控
對于我們后臺數(shù)據(jù)監(jiān)控來說,我們覺得按層次來說分成四類,每種有不同的格式和上報方式:
- 硬件層面監(jiān)控,比如服務(wù)器負(fù)載、CPU、內(nèi)存、IO、網(wǎng)絡(luò)流量等。
- 進(jìn)程運行狀態(tài),比如說消耗的內(nèi)存、CPU、IO 等。
- 模塊間調(diào)用鏈,各個模塊、機器間的調(diào)用信息,是故障定位的關(guān)鍵數(shù)據(jù)之一。
- 業(yè)務(wù)指標(biāo),業(yè)務(wù)總體層面上的數(shù)據(jù)監(jiān)控。
不同類型的數(shù)據(jù)簡化成如下格式,方便對數(shù)據(jù)進(jìn)行處理。其中底下兩層都用 IP+Key 的格式,后來出現(xiàn)了容器后,使用 ContainerID、IP、Key 的格式。
而模塊調(diào)用信息,又把模塊的被調(diào)總體信息抽出來,跟業(yè)務(wù)指標(biāo)共用 ID、Key 的數(shù)據(jù)格式。
我們重點說一下 IDKey 數(shù)據(jù)。這個 IDKey 數(shù)據(jù)是早期的重點監(jiān)控數(shù)據(jù),但其上報量占了數(shù)據(jù)上報的 9 成以上,像剛才所說,用文本型數(shù)據(jù)上報難以做到穩(wěn)定、快速。
所以我們定制了一個非常簡化、快速的上報方式,直接在內(nèi)存進(jìn)行快速匯總,具體上報方案可以看下面這個圖。
每個機器里面都申請了兩塊共享內(nèi)存,有兩塊的原因是方便進(jìn)行周期性的數(shù)據(jù)收集(6s 收集一次),每塊內(nèi)存的格式是:uint32_t[MAX_ID][MAX_KEY]。
我們內(nèi)部只允許有三種上報方式:
- 累加
- 設(shè)置新值
- 設(shè)置***值
這三種方式都是操作一個 uint32_t,性能消耗非常小,而且還有一個***的優(yōu)點,就是實時在內(nèi)存進(jìn)行匯總,每次從內(nèi)存提取的記錄只有平均 1000 條左右,大幅降低秒級統(tǒng)計的難度。
后臺數(shù)據(jù)里面還有一個重要數(shù)據(jù)是調(diào)用關(guān)系數(shù)據(jù),在故障分析定位中有非常大的作用。
具體格式如上,可以定位故障點(機器、進(jìn)程、接口)及影響面。它的上報量是小于 IDKey 的第二大數(shù)據(jù),每次后臺調(diào)用都產(chǎn)生一條數(shù)據(jù),所以使用日志方式還是很難處理。
我們在服務(wù)內(nèi)部用了另外一種跟 IDKey 接近的共享內(nèi)存統(tǒng)計方式,比如說一個服務(wù)有 N 個 Worker,每個 Worker 會分配兩塊小共享內(nèi)存進(jìn)行上報,再由收集線程對數(shù)據(jù)打包后對外發(fā)送。
這個上報是框架層進(jìn)行的上報,服務(wù)開發(fā)者不需要手工增加上報代碼(微信 99% 都是使用內(nèi)部開發(fā)的服務(wù)框架)。
終端數(shù)據(jù)監(jiān)控
后臺數(shù)據(jù)我們介紹完了,再說一下終端監(jiān)控數(shù)據(jù)。這個我們關(guān)注的是手機端的微信 APP 一些具體的性能、異常,調(diào)用微信后臺的耗時、異常,還有網(wǎng)絡(luò)異常方面的問題。
手機終端產(chǎn)生的日志數(shù)據(jù)非常巨大,如果全量上報則對終端、后臺都有不小的壓力,所以我們并沒有全量上報。
我們對不同數(shù)據(jù)、終端版本有不同的采樣配置,后臺會定期對終端下發(fā)采樣策略。
終端對數(shù)據(jù)采樣上報時也不會實時發(fā)送,而是用臨時存儲記錄下來,隔一段時間再打包發(fā)送,力求對終端的影響最小化。
對外監(jiān)控服務(wù)
下面簡單介紹一下我們***的對外監(jiān)控服務(wù),這個方案參考了一些云監(jiān)控的方案,用戶可以自行配置維度信息和配置監(jiān)控規(guī)則。
現(xiàn)在在我們的商戶管理界面還有小程序開發(fā)者工具的頁面已經(jīng)開發(fā)了這個功能,但現(xiàn)在自定義上報還沒有開放,只提供了后臺采集的一些固定數(shù)據(jù)項。
微信數(shù)據(jù)監(jiān)控的發(fā)展過程
上面介紹了數(shù)據(jù)的上報方式,接下來介紹一下我們?nèi)绾螌?shù)據(jù)進(jìn)行監(jiān)控。
異常檢測
對于一般異常檢測來說,可能都會用到三個辦法:
- 閾值,甚至在早上和晚上都是有很大差異的,這個閾值本身沒法去劃分的,所以這個對于我們來說只適用于少量的場景。
- 同比,存在的問題是我們的數(shù)據(jù)都不是每天同一時間的數(shù)據(jù)是一樣的,周一到周六會存在比較大的差異,只能降低敏感度才能保證準(zhǔn)確性。
- 環(huán)比,我們的數(shù)據(jù)中,相鄰的數(shù)據(jù)也并非平穩(wěn)變化,數(shù)量級比較小時尤其明顯,同樣只有降低敏感度才能保證準(zhǔn)確性。
所以這三種常見的數(shù)據(jù)處理方法都不是很適用我們的場景,在過去我們對算法進(jìn)行了改進(jìn)。
我們使用的***個改進(jìn)算法是均方差,就是拿過去一個月每天同一時間的數(shù)據(jù)計算平均值與均方差,用多天數(shù)據(jù)適應(yīng)數(shù)據(jù)的抖動情況。
這個算法適用范圍比較廣,但是對于波動比較大的曲線,敏感度會比較低,容易漏報。
我們改進(jìn)的第二個算法是多項式擬合預(yù)測,適用于平穩(wěn)的曲線,就有點像改進(jìn)的環(huán)比。
但如果出現(xiàn)異常時數(shù)據(jù)是平穩(wěn)增長或減少,沒有出現(xiàn)突變,這時也會判斷為正常,出現(xiàn)漏報。
所以以上兩種算法雖然比以前的算法有了不少改進(jìn),但同樣存在一些缺陷。目前我們有在嘗試其他算法,或多種算法結(jié)合一起使用。
監(jiān)控配置
除了算法本身,我們在監(jiān)控項配置也存在問題的,因為我們的服務(wù)非常多。
所以可能超過了 30 萬的監(jiān)控項要人手配置,每次配置觀察曲線選擇不同算法,不同的敏感度,而且過一段時間之后數(shù)據(jù)發(fā)生變化,需要重新調(diào)整。所以這種操作不可持續(xù)。
目前我們在嘗試對監(jiān)控項進(jìn)行自動配置,比如使用歷史數(shù)據(jù),歷史異常樣本,抽取特征,進(jìn)行數(shù)據(jù)分類,再自動套用***的監(jiān)控參數(shù)。
這個我們正在嘗試取得了一些成果,但還不是很完善,還在改進(jìn)中。
海量監(jiān)控分析下的數(shù)據(jù)存儲設(shè)計思路
上面分享了數(shù)據(jù)如何進(jìn)行采集、監(jiān)控,***再介紹一下數(shù)據(jù)是怎么存儲的。
對于我們來說數(shù)據(jù)存儲同樣重要,像剛才提到每分鐘監(jiān)控要拿一個月數(shù)據(jù)出來。
還有比如我們的故障分析,一個模塊有異常需要讀取所有機器調(diào)用信息、CPU、內(nèi)存、網(wǎng)絡(luò)、各種進(jìn)程信息等,如果機器數(shù)特別多,一次讀取的數(shù)據(jù)量會超過 50w*2 天。
所以我們對監(jiān)控數(shù)據(jù)存儲的讀寫性能要求非常高。首先寫入性能基本要求是總?cè)霂炝靠赡芤环昼娪?2 億條以上,單機至少要求 500w/min 能入到這個數(shù)據(jù)量。數(shù)據(jù)讀取性能需要能支撐每分鐘讀取 50w×22 天的監(jiān)控讀取。
數(shù)據(jù)結(jié)構(gòu)上,我們各種數(shù)據(jù)是多個維度的,比如調(diào)用關(guān)系的維度非常多,還要支持按 client 端、svr 端、模塊級、主機級等不同維度的部分匹配的查詢,不能只支持簡單的 key —— value 查詢。
注意我們的多維度 key 分成了 main key 和 sub key 兩部分,后面會有介紹為什么這樣做。
以前我們監(jiān)控數(shù)據(jù)存儲改造時參考了其他一些開源方案,但在當(dāng)時沒有找到完全符合性能、數(shù)據(jù)結(jié)構(gòu)要求的現(xiàn)成方案,所以我們自行研發(fā)了自己的時間序列服務(wù)器。
首先對數(shù)據(jù)寫入來說,如果一分鐘一條記錄,則數(shù)據(jù)量過大,所以我們會先緩存一定時間的數(shù)據(jù),隔一段時間批量合并成一天一條記錄。
這也是目前比較常用的提升寫入性能的做法。我們數(shù)據(jù)緩存的時間是一個小時。
而我們自行開發(fā)的 key-value 存儲,關(guān)鍵點是 key 的實現(xiàn)。首先 key 會常駐內(nèi)存。
另外因為數(shù)據(jù)量很大,一臺機不可能撐得住,所以我們用的是多機集群,使用 hash(main_key)對數(shù)據(jù)進(jìn)行寫入和查詢。
而部分匹配查詢是使用改造的二分查找法實現(xiàn)前置匹配查詢。 這樣實現(xiàn)的查詢性能非常高,可以超過 100w/s,而且加個查詢結(jié)果緩存性能更高。
不過它也存在一些問題,比如 hash(main_key) 數(shù)據(jù)不均衡,而且 1 天一條記錄,key 占內(nèi)存太多。
由于上面的問題,我們做了第二個改進(jìn)。
第二個改進(jìn)的方法是把 Key-Value 拆分成 key-id-value ,通過 id 分配服務(wù)控制 value 數(shù)據(jù)均衡,key-id 7 天重新分配一次,減少內(nèi)存占用。
對于存儲來說還有一個***的問題就是容災(zāi),既然是對服務(wù)器進(jìn)行監(jiān)控,自身的容災(zāi)能力要求也非常高。
一般來說做到高容災(zāi)、數(shù)據(jù)強一致性比較難,但微信后臺已經(jīng)開源了自行研發(fā)的 phxpaxos 協(xié)議框架,使用這個框架可以很容易實現(xiàn)數(shù)據(jù)容災(zāi)。
另外 phxpaxos 框架的多 master 特性可以提升并發(fā)讀取性能。
陳曉鵬,2008 年進(jìn)入騰訊,2012 年調(diào)入微信運維開發(fā)組負(fù)責(zé)運維監(jiān)控系統(tǒng)的改造,是微信當(dāng)前運維監(jiān)控系統(tǒng)的主要設(shè)計開發(fā)人員。