第一章 基礎設施,1.1 萬億交易量級下的秒級監控(做者:鬱鬆、章邯、程超、癲行)

1.1 萬億交易量級下的秒級監控

前言

2016財年,阿里巴巴電商交易額(GMV)突破3萬億元人民幣,成爲全球最大網上經濟體,這背後是基礎架構事業羣構築的堅強基石。java

在2016年雙11全球購物狂歡節中,天貓全天交易額1207億元,前30分鐘每秒交易峯值17.5萬筆,每秒支付峯值12萬筆。承載這些秒級數據背後的監控產品是如何實現的呢?接下來本文將從阿里監控體系、監控產品、監控技術架構及實現分別進行詳細講述。linux

 

1.阿里監控體系

阿里有衆多監控產品,且各產品分工明確,百花齊放。web

整個阿里監控體系以下圖:算法

集團層面的監控,以平臺爲主,所有爲阿里自主研發(除引入了第三方基調、博睿等外部檢測系統,用於各地CDN用戶體驗監控),這些監控平臺覆蓋了阿里集團80%的監控需求。windows

此外,每一個事業羣均根據自身特性自主研發了多套監控系統,以知足自身特定業務場景的監控需求,如廣告的GoldenEye、菜鳥的棱鏡、阿里雲的天基、螞蟻的金融雲(基於XFlush)、中間件的EagleEye等,這些監控系統均有各自的使用場景。多線程

阿里的監控規模早已達到了千萬量級的監控項,PB級的監控數據,億級的報警通知,基於數據挖掘、機器學習等技術的智能化監控將會愈來愈重要。架構

阿里全球運行指揮中心(GOC)基於歷史監控數據,經過異常檢測、異常標註、挖掘訓練、機器學習、故障模擬等方式,進行業務故障的自動化定位,並賦能監控中心7*24小時專業監控值班人員,使阿里集團具有第一時間發現業務指標異常,並快速進行應急響應、故障恢復的能力,將故障對線上業務的影響降到最低。併發

接下來將詳細講述本文的主角:承載阿里核心業務監控的SunFire監控平臺。框架

 

2. 監控技術實現

 

2.1 監控產品簡介

SunFire是一整套海量日誌實時分析解決方案,以日誌、REST 接口、Shell 腳本等做爲數據採集來源,提供設備、應用、業務等各類視角的監控能力,從而幫您快速發現問題、定位問題、分析問題、解決問題,爲線上系統可用率提供有效保障。運維

SunFire利用文件傳輸、流式計算、分佈式文件存儲、數據可視化、數據建模等技術,提供實時、智能、可定製、多視角、全方位的監控體系。其主要優點有:

  • 全方位實時監控:提供設備、應用、業務等各類視角的監控能力,關鍵指標秒級、普通指標分鐘級,高可靠、高時效、低延遲。
  • 靈活的報警規則:可根據業務特徵、時間段、重要程度等維度設置報警規則,實現不誤報、不漏報。
  • 管理簡單:分鐘級萬臺設備的監控部署能力,故障自動恢復,集羣可伸縮
  • 自定義便捷配置:豐富的自定義產品配置功能,便捷、高效的完成產品配置、報警配置。
  • 可視化:豐富的可視化 Dashboard,幫助您定製個性化的監控大盤。
  • 低資源佔用:在完成大量監控數據可靠傳輸的同時,保證對宿主機的 CPU、內存等資源極低佔用率。

 

2.2 監控架構

Sunfire技術架構以下:

 

2.3 監控組件介紹

針對架構圖中的各個組件,其中最關鍵的爲採集(Agent)、計算(Map、Reduce)組件,接下來將對這兩個組件進行詳細介紹。

 

2.3.1  採集

Agent負責全部監控數據的原始採集,它以 Agent 形式部署在應用系統上,負責原始日誌的採集、系統命令的執行。

 

2.3.1.1   定位

日誌原始數據的採集,按週期查詢日誌的服務,且日誌查詢要低耗、智能。Agent上不執行計算邏輯。

 

2.3.1.2    特性

 

2.3.1.2.1  低耗

採集日誌,不可避免要考慮日誌壓縮的問題,一般作日誌壓縮則意味着它必須作兩件事情:一是磁盤日誌文件的內容要讀到應用程序態;二是要執行壓縮算法。

這兩個過程就是CPU消耗的來源。可是它必須作壓縮,由於日誌須要從多個機房傳輸到集中的機房。跨機房傳輸佔用的帶寬不容小覷,必須壓縮才能維持運轉。因此低耗的第一個要素,就是避免跨機房傳輸。SunFire達到此目標的方式是運行時組件自包含在機房內部,須要全量數據時才從各機房查詢合併。

網上搜索zero-copy,會知道文件傳輸實際上是能夠不通過用戶態的,能夠在linux的核心態用相似DMA的思想,實現極低CPU佔用的文件傳輸。SunFire的Agent固然不能放過這個利好,對它的充分利用是Agent低耗的根本緣由。之前這部分傳輸代碼是用c語言編寫的sendfile邏輯,集成到java工程裏,後來被直接改造爲了java實現。

最後,在下方的計算平臺中會提到,要求Agent的日誌查詢服務具有「按週期查詢日誌」的能力。這是目前Agent工程裏最大的難題,咱們都用過RAF(RandomAccessFile),你給它一個遊標,指定offset,再給它一個長度,指定讀取的文件size,它能夠很低耗的扒出文件裏的這部份內容。然而問題在於:週期≠offset。從週期轉換爲offset是一個痛苦的過程。

在流式計算裏通常不會遇到這個問題,由於在流式架構裏,Agent是水龍頭,主動權掌握在Agent手裏,它能夠從0開始push文件內容,push到哪裏就作一個標記,下次從標記繼續日後push,不斷重複。這個標記就是offset,因此流式不會有任何問題。

而計算平臺週期任務驅動架構裏,pull的方式就沒法提供offset,只能提供Term(週期,好比2015-11-11 00:00分)。Agent解決此問題的方式算是簡單粗暴,那就是二分查找法。並且日誌還有一個自然優點,它是連續性的。因此按照對二分查找法稍加優化,就能達到「越猜越準」的效果(由於區間在縮小,區間越小,它裏面的日誌分佈就越平均)。

因而,Agent代碼裏的LogFinder組件撐起了這個職責,利用上述兩個利好,實現了一個把CPU控制在5%如下的算法,目前可以維持運轉。其中CPU的消耗不用多說,確定是來自於猜的過程,由於每一次猜想,都意味着要從日誌的某個offset拉出一小段內容來覈實,會致使文件內容進入用戶態並解析。這部分算法依然有很大的提高空間。

 

2.3.1.2.2   日誌滾動

作過Agent的同窗確定都被日誌滾動困擾過,各類各樣的滾動姿式都須要支持。SunFire的pull方式固然也會遇到這個問題,因而咱們簡單粗暴的窮舉出了某次pull可能會遇到的全部場景,好比

  • 正常猜到了offset
  • 整個日誌都猜不到offset
  • 上次猜到了offset,可是下次再來的時候發現不對勁(好比滾動了)
  • 等等等等

這段邏輯代碼窮舉的分支之多,在一開始誰都沒有想到。不過仔細分析了不少次,發現每一個分支都必不可少。

 

2.3.1.2.3   查詢接口

Agent提供的查詢服務分爲first query和ordinary query兩種。作這個區分的緣由是:一個週期的查詢請求只有第一次須要猜offset,以後只須要順序下移便可。並且計算組件裏有大量的和Agent查詢接口互相配合的邏輯,好比一個週期拉到什麼位置上算是肯定結束?一次ordinary query獲得的日誌裏若是末尾是截斷的(只有一半)該如何處理…… 這些邏輯雖然縝密,但十分繁瑣,甚至讓人望而卻步。但現狀如此,這些實現邏輯保障了SunFire的高一致性,不用擔憂數據不全、報警不許,隨便怎麼重啓計算組件,隨便怎麼重啓Agent。但這些優點的背後,是值得深思的代碼複雜度。

 

2.3.1.2.4   路徑掃描

爲了讓用戶配置簡單便捷,SunFire提供給用戶選擇日誌的方式不是手寫,而是像windows的文件夾同樣能夠瀏覽線上系統的日誌目錄和文件,讓他雙擊一個心儀的文件來完成配置。但這種便捷帶來的問題就是路徑裏如有變量就會出問題。因此Agent作了一個簡單的dir掃描功能。Agent能從應用目錄往下掃描,找到同深度文件夾下「合適」的目標日誌。

 

2.3.2  計算

由Map、Reduce組成計算平臺,負責全部採集內容的加工計算,具有故障自動恢復能力及彈性伸縮能力。

 

2.3.2.1   定位

計算平臺一直以來都是發展最快、改造最多的領域,由於它是不少需求的直接生產者,也是性能壓力的直接承擔者。所以,在通過多年的反思後,最終走向了一條插件化、週期驅動、自協調、異步化的道路。

 

2.3.2.2    特性

 

2.3.2.2.1   純異步

原來的SunFire計算系統裏,線程池繁複,從一個線程池處理完還會丟到下一個線程池裏;爲了不併發bug,加鎖也不少。這其中最大的問題有兩個:CPU密集型的邏輯和I/O密集型混合。

對於第一點,只要發生混合,不管你怎麼調整線程池參數,都會致使各式各樣的問題。線程調的多,會致使某些時刻多線程搶佔CPU,load飆高;線程調的少,會致使某些時刻全部線程都進入阻塞等待,堆積如山的活兒沒人幹。

對於第二點,最典型的例子就是日誌包合併。好比一臺Map上的一個日誌計算任務,它要收集10個Agent的日誌,那確定是併發去收集的,10個日誌包陸續(同時)到達,到達以後各自解析,解析完了data要進行merge。這個merge過程若是涉及到互斥區(好比嵌套Map的填充),就必須加鎖,不然bug滿天飛。

但其實咱們從新編排一下任務就能杜絕全部的鎖。好比上面的例子,咱們可否讓這個日誌計算任務的10個Agent的子任務,所有在同一個線程裏作?這固然是可行的,只要回答兩個問題就行:

1)若是串行,那10個I/O動做(拉日誌包)怎麼辦?串行不就浪費cpu浪費時間嗎?

2)把它們都放到一個線程裏,那我怎麼發揮多核機器的性能?

第一個問題,答案就是異步I/O。只要肯花時間,全部的I/O均可以用NIO來實現,無鎖,事件監聽,不會涉及阻塞等待。即便串行也不會浪費cpu。

第二個問題,就是一個大局觀問題了。現實中咱們面臨的場景每每是用戶配置了100個產品,每一個產品都會拆解爲子任務發送到每臺Map,而一臺Map只有4個核。因此,你讓一個核負責25個任務已經足夠榨乾機器性能了,不必追求更細粒度子任務併發。

所以,計算平臺付出了很大的精力,作了協程框架。

咱們用akka做爲協程框架,有了協程框架後不再用關注線程池調度等問題了,因而咱們能夠輕鬆的設計協程角色,實現CPU密集型和I/O密集型的分離、或者爲了無鎖而作任務編排。接下來,儘可能用NIO覆蓋全部的I/O場景,杜絕I/O密集型邏輯,讓全部的協程都是純跑CPU。按照這種方式,計算平臺已經基本可以榨乾機器的性能。

 

2.3.2.2.2  週期驅動

所謂週期驅動型任務調度,說白了就是Map/Reduce。Brain被選舉出來以後,定時撈出用戶的配置,轉換爲計算做業模型,生成一個週期(好比某分鐘的)的任務, 咱們稱之爲拓撲(Topology), 拓撲也很形象的表現出Map/Reduce多層計算結構的特徵。全部任務所需的信息,都保存在topology對象中,包括計算插件、輸入輸出插件邏輯、Map有幾個、每一個Map負責哪些輸入等等。這些信息雖然不少,但其實來源能夠簡單理解爲兩個:一是用戶的配置;二是運維元數據。拓撲被安裝到一臺Reduce機器(A)上。A上的Reduce任務判斷當前集羣裏有多少臺Map機器,就產生多少個任務(每一個任務被平均分配一批Agent),這些任務被安裝到每臺機器上Map。被安裝的Map任務其實就是一個協程,它負責了一批Agent,因而它就將每一個Agent的拉取任務再安裝爲一個協程。至此,安裝過程結束。Agent拉取任務協程(也稱之爲input輸入協程,由於它是數據輸入源)在週期到點後,開始執行,拉取日誌,解析日誌,將結果交予Map協程;Map協程在獲得了全部Agent的輸入結果並merge完成後,將merge結果回報到Reduce協程(這是一個遠程協程消息,跨機器);Reduce協程獲得了全部Map協程的彙報結果後,數據到齊,寫入到Hbase存儲,結束。

上述過程很是簡單,不高不大也不上,但通過多年大促的考驗,其實很是的務實。能解決問題的架構,就是好的架構,能簡單,爲什麼要把事情作得複雜呢?

這種架構裏,有一個很是重要的特性:任務是按週期隔離的。也就是說,同一個配置,它的2015-11-11 00:00分的任務和2015-11-11 00:01分的任務,是兩個任務,沒有任何關係,各自驅動,各自執行。理想狀況下,咱們能夠作出結論:一旦某個週期的任務結束了,它獲得的數據必然是準確的(只要每一個Agent都正常響應了)。因此採用了這種架構以後,SunFire不多再遇到數據不許的問題,當出現業務故障的時候咱們均可以判定監控數據是準確的,甚至秒級均可以判定是準確的,由於秒級也就是5秒爲週期的任務,和分鐘級沒有本質區別,只是週期範圍不一樣而已。能得到這個能力固然也要歸功於Agent的「按週期查詢日誌」的能力。

 

2.3.2.2.3  任務重試

在上節描述的Brain->Reduce->Map的任務安裝流程裏,咱們對每個上游賦予一個職責:監督下游。當機器發生重啓或宕機,會丟失一大批協程角色。每一種角色的丟失,都須要重試恢復。監督主要經過監聽Terminated事件實現,Terminated事件會在下游掛掉(不管是該協程掛掉仍是所在的機器掛掉或是斷網等)的時候發送給上游。因爲拓撲是提早生成好且具有完備的描述信息,所以每一個角色均可以根據拓撲的信息來從新生成下游任務完成重試。

若Brain丟失,則Brain會再次選主, Brain讀取最後生成到的任務週期, 再繼續生成任務

若Reduce丟失,每一個任務在Brain上都有一個TopologySupervisor角色, 來監聽Reduce協程的Terminated事件來執行重試動做

若Map丟失,Reduce自己也監聽了全部Map的Terminated事件來執行重試動做

爲確保萬無一失,若Reduce沒有在規定時間內返回完成事件給Brain,Brain一樣會根據必定規則重試這個任務。

過程依然很是簡單,並且從理論上是可證的,不管怎麼重啓宕機,均可以確保數據不丟,只不過可能會稍有延遲(由於部分任務要從新作)。

 

2.3.2.2.4   輸入共享

在用戶實際使用SunFire的過程當中,經常會有這樣的狀況:用戶配了幾個不一樣的配置,其計算邏輯多是不一樣的,好比有的是單純計算行數,有的計算平均值,有的須要正則匹配出日誌中的內容,但這幾個配置可能都用的同一份日誌,那麼必定但願幾個配置共享同一份拉取的日誌內容。不然重複拉取日誌會形成極大的資源消耗。

那麼咱們就必須實現輸入共享,輸入共享的實現比較複雜,主要依賴兩點:

其一是依賴安裝流,由於拓撲是提早安裝的,所以在安裝到真正開始拉取日誌的這段時間內,咱們但願可以經過拓撲信息判斷出須要共享的輸入源,構建出輸入源和對應Map的映射關係。

其二是依賴Map節點和Agent之間的一致性哈希,保證Brain在生成任務時,同一個機器上的日誌,永遠是分配相同的一個Map節點去拉取的(除非它對應的Map掛了)。

站在Map節點的視角來看:在各個任務的Reduce拿着拓撲來註冊的時候,我拿出輸入源(對日誌監控來講一般能夠用一個IP地址和一個日誌路徑來描述)和Map之間的關係,暫存下來,每當一個新的Reduce來註冊Map,我都判斷這個Map所需的輸入源是否存在了,若是有,那就給這個輸入源增長一個上游,等到這個輸入源的週期到了,那就觸發拉取,再也不共享了。

 

2.3.3   其餘組件

存儲:負責全部計算結果的持久化存儲,能夠無限伸縮,且查詢歷史數據保持和查詢實時數據相同的低延遲。Sunfire原始數據存儲使用的是阿里集團的Hbase產品(HBase :Hadoop Database,是一個高可靠性、高性能、面向列、可伸縮的分佈式存儲系統),用戶配置存儲使用的是MongoDb。

展現:負責提供用戶交互,讓用戶經過簡潔的建模過程來打造個性化的監控產品。基於插件化、組件化的構建方式,用戶能夠快速增長新類型的監控產品。

自我管控:即OPS-Agent、Ops-web組件,負責海量Agent的自動化安裝監測,而且承擔了整個監控平臺各個角色的狀態檢測、一鍵安裝、故障恢復、容量監測等職責。

 

2.4 秒級監控產品化

目前SunFire已將秒級監控能力產品化,將秒級監控能力賦能給研發運維,用戶可自行根據實際業務監控需求進行配置,操做簡單便捷,很是靈活。

效果圖以下:

相關文章
相關標籤/搜索