Spark Streaming + Elasticsearch構建App異常監控平臺

鏈接:https://zhuanlan.zhihu.com/p/23657497
來源:知乎

如果在使用App時遇到閃退,你可能會選擇卸載App、到應用商店怒斥開發者等方式來表達不滿。但開發者也同樣感到頭疼,因爲崩潰可能意味着用戶流失、營收下滑。爲了降低崩潰率,進而提升App質量,App開發團隊需要實時地監控App異常。一旦發現嚴重問題,及時進行熱修復,從而把損失降到最低。App異常監控平臺,就是將這個方法服務化。

低成本

小型創業團隊一般會選擇第三方平臺提供的異常監控服務。但中型以上規模的團隊,往往會因爲不想把核心數據共享給第三方平臺,而選擇獨立開發。造輪子,首先要考慮的就是成本問題。我們選擇了站在開源巨人的肩膀上,如圖1所示。

Spark Streaming

每天來自客戶端和服務器的大量異常信息,會源源不斷的上報到異常平臺的Kafka中,因此我們面臨的是一個大規模流式數據處理問題。美團點評數據平臺提供了Storm和Spark Streaming兩種流式計算解決方案。我們主要考慮到團隊之前在Spark批處理方面有較多積累,使用Spark Streaming成本較低,就選擇了後者。

Elasticsearch

Elasticsearch(後文簡稱ES),是一個開源搜索引擎。不過在監控平臺中,我們是當做“數據庫”來使用的。爲了降低展示層的接入成本,我們還使用了另一個開源項目ES SQL提供類SQL查詢。ES的運維成本,相對 SQL on HBase方案也要低很多。整個項目開發只用了不到700行代碼,開發維護成本還是非常低的。那如此“簡單”的系統,可用性可以保證嗎?

高可用

Spark Streaming + Kafka的組合,提供了“Exactly Once”保證:異常數據經過流式處理後,保證結果數據中(注:並不能保證處理過程中),每條異常最多出現一次,且最少出現一次。保證Exactly Once是實現24/7的高可用服務最困難的地方。在實際生產中會出現很多情況,對Exactly Once的保證提出挑戰:

異常重啓

Spark提供了Checkpoint功能,可以讓程序再次啓動時,從上一次異常退出的位置,重新開始計算。這就保證了即使發生異常情況,也可以實現每條數據至少寫一次HDFS。再覆寫相同的HDFS文件就保證了Exactly Once(注:並不是所有業務場景都允許覆寫)。寫ES的結果也一樣可以保證Exactly Once。你可以把ES的索引,就當成HDFS文件一樣來用:新建、刪除、移動、覆寫。
作爲一個24/7運行的程序,在實際生產中,異常是很常見的,需要有這樣的容錯機制。但是否遇到所有異常,都要立刻掛掉再重啓呢?顯然不是,甚至在一些場景下,你即使重啓了,還是會繼續掛掉。我們的解決思路是:儘可能把異常包住,讓異常發生時,暫時不影響服務。

如圖2所示,包住異常,並不意味可以忽略它,必須把異常收集到Spark Driver端,接入監控(報警)系統,人工判斷問題的嚴重性,確定修復的優先級。
爲了更好地掌控Spark Streaming服務的狀態,我們還單獨開發了一個作業調度(重啓)工具。美團點評數據平臺安全認證的有效期是7天,一般離線的批處理作業很少會運行超過這個時間,但Spark Streaming作業就不同了,它需要一直保持運行,所以作業只要超過7天就會出現異常。因爲沒有找到優雅的解決方案,只好粗暴地利用調度工具,每週重啓刷新安全認證,來保證服務的穩定。

升級重導

Spark提供了2種讀取Kafka的模式:“Receiver-based Approach”和“Direct Approach”。使用Receiver模式,在極端情況下會出現Receiver OOM問題。
使用Direct模式可以避免這個問題。我們使用的就是這種Low-level模式,但在一些情況下需要我們自己維護Kafka Offset:
升級代碼:開啓Checkpoint後,如果想改動代碼,需要清空之前的Checkpoint目錄後再啓動,否則改動可能不會生效。但當這樣做了之後,就會發現另一個問題——程序“忘記”上次讀到了哪個位置,因爲存儲在Checkpoint中的Offset信息也一同被清空了。這種情況下,需要自己用ZooKeeper維護Kafka的Offset。
重導數據:重導數據的場景也是,當希望從之前的某一個時間點開始重新開始計算的時候,顯然也需要自己維護時間和Offset的映射關係。
自己維護Offset的成本並不高,所以看起來Checkpoint功能很雞肋。其實可以有一些特殊用法的,例如,因爲Python不需要編譯,所以如果使用的是PySpark,可以把主要業務邏輯寫在提交腳本的外邊,再使用Import調用。這樣升級主要業務邏輯代碼時,只要重啓一下程序即可。網上有不少團隊分享過升級代碼的“黑科技”,這裏不再展開。
實現24/7監控服務,我們不僅要解決純穩定性問題,還要解決延遲問題。

低延遲

App異常監控,需要保證數據延遲在分鐘級。
雖然Spark Streaming有着強大的分佈式計算能力,但要滿足用戶角度的低延遲,可不是單純的能計算完這麼簡單。

輸入問題

iOS App崩潰時,會生成Crash Log,但其內容是一堆十六進制的內存地址,對開發者來說就是“天書”。只有經過“符號化”的Crash Log,開發者才能看懂。因爲符號化需要在Mac環境下進行,而我們的Mac集羣資源有限,不能符號化全部Crash Log。即使做了去重等優化,符號化後的數據流還是有延遲。每條異常信息中,包含N維數據,如果不做符號化只能拿到其中的M維。

如圖3所示,我們將數據源分爲符號化數據流、未符號化數據流,可以看出兩個數據流的相對延遲時間T較穩定。如果直接使用符號化後的數據流,那麼全部N維數據都會延遲時間T。爲了降低用戶角度的延遲,我們根據經驗加大了時間窗口:先存儲未符號化的M維數據,等到拿到對應的符號化數據後,再覆寫全部N維數據,這樣就只有N-M維數據延遲時間T了。

輸出問題

如果Spark Streaming計算結果只是寫入HDFS,很難遇到什麼性能問題。但你如果想寫入ES,問題就來了。因爲ES的寫入速度大概是每秒1萬行,只靠增加Spark Streaming的計算能力,很難突破這個瓶頸。
異常數據源的特點是數據量的波峯波谷相差巨大。由於我們使用了 Direct 模式,不會因爲數據量暴漲而掛掉,但這樣的“穩定”從用戶角度看沒有任何意義:短時間內,數據延遲會越來越大,暴增後新出現的異常無法及時報出來。爲了解決這個問題,我們制定了一套服務降級方案。

如圖4所示,我們根據寫ES的實際瓶頸K,對每個週期處理的全部數據N使用水塘抽樣(比例K/N),保證始終不超過瓶頸。並在空閒時刻使用Spark批處理,將N-K部分從HDFS補寫到ES。既然寫ES這麼慢,那我們爲什麼還要用ES呢?

高性能

開發者需要在監控平臺上分析異常。實際分析場景可以抽象描述爲:“實時 秒級 明細 聚合” 數據查詢。
我們團隊在使用的OLAP解決方案可以分爲4種,它們各有各的優勢:

SQL on HBase方案,例如:Phoenix、Kylin。我們團隊從2015年Q1開始,陸續在SEM、SEO生產環境中使用Phoenix、Kylin至今。Phoenix算是一個“全能選手”,但更適合業務模式較固定的場景;Kylin是一個很不錯的OLAP產品,但它的問題是不能很好支持實時查詢和明細查詢,因爲它需要離線預聚合。另外,基於其他NoSQL的方案,基本大同小異,如果選擇HBase,建議團隊在HBase運維方面有一定積累。
SQL on HDFS方案,例如:Presto、Spark SQL。這兩個產品,因爲只能做到亞秒級查詢,我們平時多用在數據挖掘的場景中。
時序數據庫方案,例如:Druid、OpenTSDB。OpenTSDB是我們舊版App異常監控系統使用過的方案,更適合做系統指標監控。
搜索引擎方案,代表項目有ES。相對上面的3種方案,基於倒排索引的ES非常適合異常分析的場景,可以滿足:實時、秒級、明細、聚合,全部4種需求。
ES在實際使用中的表現如何呢?

明細查詢

支持明顯查詢,算是ES的主要特色,但因爲是基於倒排索引的,明細查詢的結果最多隻能取到10000條。在異常分析中,使用明細查詢的場景,其實就是追查異常Case,根據條件返回前100條就能滿足需求了。例如:已知某設備出現了Crash,直接搜索這個設備的DeviceId就可以看到這個設備最近的異常數據。我們在生產環境中做到了95%的明細查詢場景1秒內返回。

聚合查詢

面對爆炸的異常信息,一味追求全是不現實,也是沒必要的。開發者需要能快速發現關鍵問題。
因此平臺需要支持多維度聚合查詢,例如按模塊版本機型城市等分類聚合,如圖5所示。

不用做優化,ES聚合查詢的性能就已經可以滿足需求。因此,我們只做了一些小的使用改進,例如:很多異常數據在各個維度的值都是相同的,做預聚合可以提高一些場景下的查詢速度。開發者更關心最近48小時發生的異常,分離冷熱數據,自動清理歷史數據也有助於提升性能。最終在生產環境中,做到了90%的聚合查詢場景1秒內返回。

可擴展

異常平臺不止要監控App Crash,還要監控服務端的異常、性能等。不同業務的數據維度是不同的,相同業務的數據維度也會不斷的變化,如果每次新增業務或維度都需要修改代碼,那整套系統的升級維護成本就會很高。

維度

爲了增強平臺的可擴展性,我們做了全平臺聯動的動態維度擴展:如果App開發人員在日誌中新增了一個“城市”維度,那麼他不需要聯繫監控平臺做項目排期,立刻就可以在平臺中查詢“城市”維度的聚合數據。只需要制定好數據收集、數據處理、數據展示之間的交互協議,做到動態維度擴展就很輕鬆了。需要注意的是,ES中需要聚合的維度,Index要設置爲“not_analyzed”。
想要支持動態字段擴展,還要使用動態模板,樣例如下:

{
“mappings”: {
“es_type_name”: {
“dynamic_templates”: [
{
“template_1”: {
“match”: “log“,
“match_mapping_type”: “string”,
“mapping”: {
“type”: “string”
}
}
},
{
“template_2”: {
“match”: “*”,
“match_mapping_type”: “string”,
“mapping”: {
“type”: “string”,
“index”: “not_analyzed”
}
}
}
]
}
}
}
資源

美團點評數據平臺提供了Kafka、Spark、ES的集羣,整套技術棧在資源上也是分佈式可擴展的。
線上集羣使用的版本:

kafka-0.8.2.0
spark-1.5.2
elasticsearch-2.1.1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章