關於大型監控系統的高效能元件設計

天府雲創發表於2018-01-26

以前有幸參與過一個分散式監控系統開發,有一些所謂的設計和開發的經驗,但苦於公司的內部系統無法開源,所以也就藏著掖著。   前兩天跟一途牛網、餓了麼的朋友胡扯了監控系統的事,這尼瑪正好點燃了我這高射炮,可以有個地好好釋放了下。 記得13年的時候,跟金山獵豹的斌哥聊過一些設計方案,後來聽他說也借鑑了一些思路,真假就不知道了。  趁著現在腦袋還清閒,開源的監控系統還沒有注意這一片思路前,先把自己的經驗共享出來。  這樣,如果有其他公司朋友如果想實現高效能的監控,或者是擴充套件現存的監控效能瓶頸, 可以適當的參考下我這文章。  

首先老生常談一下分散式監控系統的簡單流程. 


流程圖:

           –>   register
agent   –>   proxy   –>   judge   –>   alarm   –>  store   –>  前端  

對於監控系統的瓶頸解決過程,我深有體會….   這裡說個我經歷過的事情,我以前公司的zabbix監控系統就管理了將近3w+伺服器,包含雲主機。 對的,是3w個主機.  結果跟你想的一樣,確實經常出問題。 問題很是多樣化,有mysql io的問題,有zabbix server效能問題,有proxy卡頓,報警不及時。 總之造成這些問題的原因是量級過大引起的。


那麼後來是怎麼解決的? 

mysql這一段做了分庫分表,儲存換成了ssd,資料庫引擎也更換為tokudbd。 報警不再採用同步阻塞的方式,而是使用我開發的report系統來傳送。 對於proxy中繼服務掛了咋辦? 明顯是相關的客戶端太多了,就做了一些分流,多開了幾個proxy。 對於zabbix server的瓶頸是怎麼解決的? 不好意思,沒在程式碼層次解決。 是根據業務線拆分成到不同的zabbix裡,也就是說多開了一組zabbix server。


那麼然後呢?

確實解決了這些問題,那麼為什麼沒有在程式碼層次解決?那麼他的效能瓶頸在哪裡?  我上面有說過,我有分散式監控系統的開發及優化的驗,所以很容易想到,也能找到他的瓶頸,比如閾值判斷回掃資料庫, 資料沒有聚合歸檔, 執行緒使用不合理等等…. 這本身是很不合理的設計。。。我自己也大範圍的改過zabbix server程式碼,最後因為各種問題不得不放棄。  說實話真心沒必要把時間耗盡在這zabbix裡,zabbix的程式碼有些雜亂不是很好理解,好在我沒花多少時間放在zabbix上。   大多數公司到了一定級別後,都會選擇性的開發監控系統,我一直都覺得監控系統開發說難也難,說簡單也簡單,這是廢話….   只要把幾個瓶頸點和功能點想明白後,這都不是問題…    其實不管用是python,golang實現,都離不開對於監控系統的理解和優化。 

另外,zabbix其實也是個商業組織,他們本身有zabbix優化方案的,雖然沒有說實現過程,但點出了他們使用了那些技術,比如redis,分散式任務佇列…  

不扯了,說正題


對於監控的客戶來說,你需要實現這麼幾個功能.

  • 註冊主機,併傳送心跳包
  • 收集客戶端的各種資料,   系統類的資料可以從/proc拿到
  • 需要開啟一個對localhost的介面,使用者可以自定義區sender資料.
  • 預先設定工作執行緒數目,可根據任務的數量動態建立
  • 需要設計一個良好的任務佇列,可以理解為任務生成器.
  • 定時去獲取最新的監控列表及外掛
    • 非同步, 可以使用condition的方式通知另一個執行緒去更新列表及外掛資訊並更正running , wait 任務配對資訊.
    • 同步, 工作執行緒拿到任務後, 可檢視記憶體中該任務對應的狀態(繼續,停止,週期變化,有更新),再操作。
  • agent需要有一個schduler的角色,來收縮擴充套件任務的變更.
  • push邏輯一定要單獨寫,這樣解耦的目的是,工作執行緒只需要收集資料就可以了. 
  • 收集監控資料一定要做好timeout的配置,對於/proc不需要,涉及到網路io及大範圍掃描時,需加timeout.
  • 模組內部可實現reload及自省反射模式

對於proxy中繼元件來說,他是減少服務端直接跟客戶端建立連結,在多個資料中心場景下,每個節點都跟server端建立連線也不是個好主意。  強烈推薦大家在proxy端進行judge判斷,然後把報警這個事告訴alarm,至於單條資訊實時推送,歸檔彙總計算完了後,會伴隨推鬆過去。   在proxy進行judege判斷,解決了server不小的壓力。


對於judge元件來說,他主要是實現判斷客戶端提交的資料是否達到了閾值條件,是則觸發alarm.   judge系統不可能簡單就判斷一次,也肯定不能就判斷一次,這樣很容易就造成報警氾濫。 我們需要實現下面幾個功能 

  • 簡單判斷
  • 時間區間次數判斷
  • 多久內只發一次


在詳細描述他之前,我們要知道judge的不合理會成為server端的一個效能瓶頸, 比如zabbix每次判斷狀態的時候都回掃資料庫….  你完全可以把資料放到快取裡面,但你們肯定會問連續三次觸發報警,怎麼實現…  60秒內觸發三次閾值怎麼判斷.    你可以使用redis sorted set來實現,把時間戳作為score,數值作為值. 

這裡提一下服務序號產生器制,為什麼需要服務序號產生器制?   當時開發這元件的主要目的是為了收集客戶端的心跳,如果他沒有心跳了,那麼就說明他掛了或者是網路有異常.

我們舉個例子,你監控流量,server 端收集到了後,可以進行閾值判斷。 但是如果過了很久還是沒有傳送給你,你靠什麼來判斷閾值,或者是 得到他長時間沒給我資訊這個事件。    我們這裡需要明確一個點, 在開發監控系統的時候,就要設計server端的timer事件只關心agent心跳,對於該客戶端的記憶體,流量,負載都不會註冊timer事件。  因為我只要知道你沒來心跳,那麼別的資訊也過不來。 

服務註冊還可以進行服務歸類,對於業務專案來說,可以看到他所屬的專案的服務樹狀態,也就是說可以看到專案的整體狀態。 這裡不限於agent級別,你可以讓多個程式都註冊上來,如果某個程式被oom了,那麼你可以在前端的服務樹裡看到他的消失。 這裡可以使用 zookeeper,etcd做服務註冊, 我推薦使用etcd,雖然etcd沒有zookeeper那種臨時session的概念,但ttl timeout足夠用了,另外etcd的原始碼實現也夠簡單和高效,很是養眼。    友情提示下,redis是沒有那種expire watch機制,因為redis 過期key掃描力度很粗,在stackoverflow看過一個回覆,說是在redis裡把expire的掃描任務改成每秒執行,然後使用brpop事件的方式來達到watch,有些意思。 

alarm ,就是報警,沒啥好說的,只需要跟judge解耦構建成非同步方式就可以了,另外別像zabbix那樣把事件放到mysql,這樣多個執行緒去讀取的時候,還要考慮資料安全問題。

  • 微信
  • 郵件
  • 簡訊
  • 電話


store,  簡單說他是入庫的模組,高階說法是歸檔聚合計算模組.  可想而知,高階說法明顯複雜的多.  很多監控系統的前段為毛那麼慢? 就算用了ssd之後雖然有明顯提高,但瓶頸出現在mysql計算上。  比如5秒一個資料,你想看一個月的圖表趨勢,怎麼算?很明顯這要經過大量的臨時的聚合計算。 我們不能怎麼搞? 我們需要提前入庫的時候,就要計算好聚合點。 比如一堆的5秒資料,我們按照他們當前的分鐘數來聚合成15秒點,30秒的點,60秒的點….


監控系統的聚合歸檔怎麼實現?


還是使用redis sorted set有序佇列,每個主機的監控專案為一個佇列,這個佇列可以跟judge一樣.   取一分鐘,然後15 秒一組,30  一組,60秒一組,計算avg後入庫。  


為什麼不用45秒? 因為涉及到多個分鐘段。 不好實現。


怕影響時間區間閾值判斷,可以去前三分鐘之前的一段資料。  每次都是 0- 三分前 !

那麼最後歸檔的資料放在哪裡, mysql ?   ElasticSearch , opentsdb, influxdb ?


mysql在量級不是很大的時候是沒有問題,當出現效能瓶頸可以讀寫分離,再不行可以對歷史表進行時間分表。 記得在邏輯裡多加cache,如果條件可以的話,直接用ssd. 


influxdb我用過很深,在樂視的時候就在用,也看過部分的程式碼,因為是golang實現的,程式碼很是整潔,看程式碼的過程中也學到了架構上的設計。  最後我對他的印象是不堪大用,如果你的資料過500G,不管你使用leveldb,rocksdb,hyperdb都有些徒勞,效能縮水很厲害.  至於他的分散式? 我就不說了,我寫過不少influxdb的文章,大家可以看看, http://xiaorui.cc/tag/influxdb/ 


大多數公司都會採用mysql,如果你的公司有Elasticsearch和hbase的基礎服務,那麼你可以在這基礎上構建監控儲存系統。  如果沒有這些基礎環境,單純為了監控系統起這些服務,後期又擴充套件,不好說,看公司支援力度了。


我對於hbase和Elasticsearch都用了將今兩年的時間, 不太推薦使用hbase,這裡說明下我們不是直接使用hbase,而是使用在hbase之上封裝的opentsdb.   opentsdb就是為了監控服務而存在的.   如果你的系統不是java開發的,那麼操作hbase不外乎中介軟體的方法,像thrift server,opentsdb的tsd都可以算是代理,thrift提供的是最原始的hbase的方法,而opentsdb提供了類似監控模板的方法,又因為opentsdb的效能不弱,所以是有不少公司選擇它,比如阿里, 當然人家那java黨多,神也多,可以深度挖坑埋坑….


hbase本身是不支援二級索引的,他的索引只有rowkey行健,往往rowkey裡含有主機的標識資訊及時間資訊,像這樣該組監控資料產生了時序或者單調(遞增/遞減)行鍵,導致連續到來的資料會被分配到統一region中,一堆資料集中到一個region,雖然不能更用應用hbase的優勢,但這樣介紹了hbase之間的一些協作,像監控資料本身也沒啥大量計算,另外使用hbase的時候,預先要設定region,要不然每次都要分裂分配region很是影響效能。  你每次從hbase想獲取一個時間段資料,都是scan的過程,如果你想做一些各方面的統計,那就要寫Mapreduce了。


我這裡推薦大家使用ElasticSearch作為監控資料的儲存,他的好處多多,首先他的查詢效能沒得說,可以像hbase那樣橫向擴充套件,可以像mysql那樣多個欄位查詢,可以隨意搭配欄位進行查詢,圖示展現可以直接使用grafana,kibana,經過一年的使用發現,grafana真的很適合做監控系統的前端展現。   這時候對於你來說,只需要開發系統系統的管理頁面就可以了,當然如果要規範化,儘量還是自己開發監控的前端。  不要試圖修改grafana的前端原始碼,不容易呀,教訓呀。

記得給query加快取,以後資料可以從redis獲取,需要控制好快取的超時,對於一個小時之前的資料,可以快取個10天,對於當前的資料可以選擇性的快取。

總的來說描述有些粗了, 有時間再好好細化下,由於篇幅原因沒有聊到前端方面的優化,後期陸續更新。 

相關文章