揭秘|每秒千萬級的實時資料處理是怎麼實現的?

閒魚技術發表於2022-12-07


01
背景

閒魚目前實際生產部署環境越來越複雜,橫向依賴各種服務盤宗錯節,縱向依賴的執行環境也越來越複雜。當服務出現問題的時候,能否及時在海量的資料中定位到問題根因,成為考驗閒魚服務能力的一個嚴峻挑戰。


線上出現問題時常常需要十多分鐘,甚至更長時間才能找到問題原因,因此一個能夠快速進行自動診斷的系統需求就應用而生,而快速診斷的基礎是一個高效能的實時資料處理系統。這個實時資料處理系統需要具備如下的能力:
1、資料實時採集、實時分析、複雜計算、分析結果持久化。
2、可以處理多種多樣的資料。包含應用日誌、主機效能監控指標、呼叫鏈路圖。
3、高可靠性。系統不出問題且資料不能丟。
4、高效能,底延時。資料處理的延時不超過3秒,支援每秒千萬級的資料處理。
      本文不涉及問題自動診斷的具體分析模型,只討論整體實時資料處理鏈路的設計。


02
輸入輸出定義

       為了便於理解系統的運轉,我們定義該系統整體輸入和輸出如下:

輸入:

服務請求日誌(包含traceid、時間戳、客戶端ip、服務端ip、耗時、返回碼、服務名、方法名)

環境監控資料(指標名稱、ip、時間戳、指標值)。比如cpu、 jvm gc次數、jvm gc耗時、資料庫指標。


輸出:
        一段時間內的某個服務出現錯誤的根因,每個服務的錯誤分析結果用一張有向無環圖表達。(根節點即是被分析的錯誤節點,葉子節點即是錯誤根因節點。葉子節點可能是一個外部依賴的服務錯誤也可能是jvm異常等等)。


03
架構設計

      在實際的系統執行過程中,隨著時間的推移,日誌資料以及監控資料是源源不斷的在產生的。每條產生的資料都有一個自己的時間戳。而實時傳輸這些帶有時間戳的資料就像水在不同的管道中流動一樣。

揭秘|每秒千萬級的實時資料處理是怎麼實現的?

     如果把源源不斷的實時資料比作流水,那資料處理過程和自來水生產的過程也是類似的:                 


揭秘|每秒千萬級的實時資料處理是怎麼實現的?

自然地,我們也將實時資料的處理過程分解成採集、傳輸、預處理、計算、儲存幾個階段。

整體的系統架構設計如下:                         

揭秘|每秒千萬級的實時資料處理是怎麼實現的?

採集

        採用阿里自研的sls日誌服務產品(包含logtail+loghub元件),logtail是採集客戶端,之所以選擇logtail是因為其優秀的效能、高可靠性以及其靈活外掛擴充套件機制,閒魚可以定製自己的採集外掛實現各種各樣資料的實時採集。

傳輸

        loghub可以理解為一個資料釋出訂閱元件,和kafka的功能類似,作為一個資料傳輸通道其更穩定、更安全,詳細對比文章參考:

預處理

        實時資料預處理部分採用blink流計算處理元件(開源版本叫做flink,blink是阿里在flink基礎上的內部增強版本)。目前常用的實時流計算開源產品有Jstorm、SparkStream、Flink。Jstorm由於沒有中間計算狀態的,其計算過程中需要的中間結果必然依賴於外部儲存,這樣會導致頻繁的io影響其效能;SparkStream本質上是用微小的批處理來模擬實時計算,實際上還是有一定延時;Flink由於其出色的狀態管理機制保證其計算的效能以及實時性,同時提供了完備SQL表達,使得流計算更容易。

計算與持久化

       資料經過預處理後最終生成呼叫鏈路聚合日誌和主機監控資料,其中主機監控資料會獨立儲存在tsdb時序資料庫中,供後續統計分析。tsdb由於其針對時間指標資料的特別儲存結構設計,非常適合做時序資料的儲存與查詢。呼叫鏈路日誌聚合資料,提供給cep/graph service做診斷模型分析。cep/graph service是閒魚自研的一個應用,實現模型分析、複雜的資料處理以及外部服務進行互動,同時藉助rdb實現圖資料的實時聚合。
       最後cep/graph service分析的結果作為一個圖資料,實時轉儲在lindorm中提供線上查詢。lindorm可以看作是增強版的hbase,在系統中充當持久化儲存的角色。


04
詳細設計與效能最佳化

採集


      日誌和指標資料採集使用logtail,整個資料採集過程如圖:

揭秘|每秒千萬級的實時資料處理是怎麼實現的?

其提供了非常靈活的外掛機制,共有四種型別的外掛:

  • inputs: 輸入外掛,獲取資料。

  • processors: 處理外掛,對得到的資料進行處理。

  • aggregators: 聚合外掛,對資料進行聚合。

  • flushers: 輸出外掛,將資料輸出到指定 sink。

由於指標資料(比如cpu、記憶體、jvm指標)的獲取需要呼叫本地機器上的服務介面獲取,因此應儘量減少請求次數,在logtail中,一個input佔用一個goroutine。閒魚透過定製input外掛和processors外掛,將多個指標資料(比如cpu、記憶體、jvm指標)在一個input外掛中透過一次服務請求獲取(指標獲取介面由基礎監控團隊提供),並將其格式化成一個json陣列物件,在processors外掛中再拆分成多條資料,以減少系統的io次數同時提升效能。

傳輸

     資料傳輸使用LogHub,logtail寫入資料後直接由blink消費其中的資料,只需設定合理的分割槽數量即可。分割槽數要大於等於blink讀取任務的併發數,避免blink中的任務空轉。

預處理

預處理主要採用blink實現,主要的設計和最佳化點:

編寫高效的計算流程

blink是一個有狀態的流計算框架,非常適合做實時聚合、join等操作。
在我們的應用中只需要關注出現錯誤的的請求上相關服務鏈路的呼叫情況,因此整個日誌處理流分成兩個流:
1、服務的請求入口日誌作為一個單獨的流來處理,篩選出請求出錯的資料。
2、其他中間鏈路的呼叫日誌作為另一個獨立的流來處理,透過和上面的流join on traceid實現出錯服務依賴的請求資料塞選。
揭秘|每秒千萬級的實時資料處理是怎麼實現的?

       如上圖所示透過雙流join後,輸出的就是所有發生請求錯誤相關鏈路的完整資料。

設定合理的state生命週期

       blink在做join的時候本質上是透過state快取中間資料狀態,然後做資料的匹配。而如果state的生命週期太長會導致資料膨脹影響效能,如果state的生命週期太短就會無法正常關聯出部分延遲到來的資料,所以需要合理的配置state生存週期,對於該應用允許最大資料延遲為1分鐘。

使用niagara作為statebackend,以及設定state資料生命週期,單位毫秒state.backend.type=niagarastate.backend.niagara.ttl.ms=60000

開啟MicroBatch/MiniBatch

       MicroBatch 和 MiniBatch 都是微批處理,只是微批的觸發機制上略有不同。原理上都是快取一定的資料後再觸發處理,以減少對 state 的訪問從而顯著提升吞吐,以及減少輸出資料量。

開啟joinblink.miniBatch.join.enabled=true使用 microbatch 時需要保留以下兩個 minibatch 配置blink.miniBatch.allowLatencyMs=5000防止OOM,每個批次最多快取多少條資料blink.miniBatch.size=20000

動態負載使用Dynamic-Rebalance替代Rebalance

blink任務在執行是最忌諱的就是存在計算熱點,為保證資料均勻使用Dynamic Rebalance,它可以根據當前各subpartition中堆積的buffer的數量,選擇負載較輕的subpartition進行寫入,從而實現動態的負載均衡。相比於靜態的rebalance策略,在下游各任務計算能力不均衡時,可以使各任務相對負載更加均衡,從而提高整個作業的效能。

開啟動態負載task.dynamic.rebalance.enabled=true

自定義輸出外掛

        資料關聯後需要將統一請求鏈路上的資料作為一個資料包通知下游圖分析節點,傳統的方式的是透過訊息服務來投遞資料。但是透過訊息服務有兩個缺點:
1、其吞吐量和rdb這種記憶體資料庫相比比還是較大差距(大概差一個數量級)。
2、在接受端還需要根據traceid做資料關聯。
      我們透過自定義外掛的方式將資料透過非同步的方式寫入RDB,同時設定資料過期時間。在RDB中以

圖聚合計算

      cep/graph計算服務節點在接收到metaQ的通知後,綜合根據請求的鏈路資料以及依賴的環境監控資料,會實時生成診斷結果。診斷結果簡化為如下形式:

揭秘|每秒千萬級的實時資料處理是怎麼實現的?

       說明本次請求是由於下游jvm的執行緒池滿導致的,但是一次呼叫並不能說明該服務不可用的根本原因,需要分析整體的錯誤情況,那就需要對圖資料做實時聚合。
       聚合設計如下(為了說明基本思路,做了簡化處理):
       1、首先利用redis的zrank能力為根據服務名或ip資訊為每個節點分配一個全域性唯一排序序號。
       2、為圖中的每個節點生成對應圖節點編碼,編碼格式:
-對於頭節點:頭節點序號|歸整時間戳|節點編碼
-對於普通節點:|歸整時間戳|節點編碼
       3、由於每個節點在一個時間週期內都有唯一的key,因此可以將節點編碼作為key利用redis為每個節點做計數。同時消除了併發讀寫的問題。
       4、利用redis中的set集合可以很方便的疊加圖的邊。
       5、記錄根節點,即可透過遍歷還原聚合後的圖結構。
       聚合後的結果大致如下:

揭秘|每秒千萬級的實時資料處理是怎麼實現的?

       這樣最終生成了服務不可用的整體原因,並且透過葉子節點的計數可以實現根因的排序。


05
收益

        系統上線後,整個實時處理資料鏈路的延遲不超過三秒。閒魚服務端問題的定位時間從十多分鐘甚至更長時間下降到五秒內。大大的提升了問題定位的效率。

06
展望

        目前的系統可以支援閒魚每秒千萬的資料處理能力。後續自動定位問題的服務可能會推廣到阿里內部更多的業務場景,隨之而來的是資料量的成倍增加,因此對於效率和成本提出了更好的要求。


       未來我們可能做的改進:
1、能夠自動的減少或者壓縮處理的資料。
2、複雜的模型分析計算也可以在blink中完成,減少io,提升效能。
3、支援多租戶的資料隔離。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900359/viewspace-2649461/,如需轉載,請註明出處,否則將追究法律責任。

相關文章