訊息服務 + Serverless 函式計算如何助力企業降本提效?

Serverless發表於2023-01-11

作者 | 柳下

背景介紹

訊息佇列服務(下文均以 Message Service 命名)作為雲端計算 PaaS 領域的基礎設施之一,其高併發、削峰填谷的特性愈發受到開發者關注。Message Service 對上承接訊息生產者服務的請求,對下連線消費者服務。提到消費:那就不得不引入兩個問題?

  1. 如何以低成本、高吞吐、低延時的方式將訊息資料從 Message Service 輸送給下游消費服務?
  2. 如何快速構建免運維、按需彈性伸縮算力的訊息消費服務?

今天就來聊聊如何在阿里雲上基於 Serverless 計算服務 + Message Service 構建這樣一套系統。

名詞解釋

函式計算(Function Compute)

阿里雲函式計算是事件驅動的全託管 Serverless 計算服務。透過函式計算,您無需管理伺服器等基礎設施,只需編寫程式碼並上傳。函式計算會為您準備好計算資源,以彈性、可靠的方式執行您的程式碼,更多產品細節可閱讀官方文件[1]。
https://help.aliyun.com/product/50980.html

聯結器(Connector)

Connector 實現了大量資料的匯入和匯出。例如將 KAFKA topic 中資料匯出到 stdout,或將本地檔案中資料匯入到 RocketMQ。Connector 簡化了資料在不同系統間複製和傳輸的複雜度,本文探討的訊息服務和計算服務的連線同樣依賴 Connector 實現。

事件匯流排(EventBridge)

事件匯流排是 Connector 的產品化服務,支援阿里雲服務、自定義應用、SaaS 應用等以標準化、中心化的方式接入,並能夠以標準化協議在這些應用之間路由事件,幫助您輕鬆構建松耦合、分散式的事件驅動架構,更多產品細節可閱讀官方文件[2]。
https://help.aliyun.com/product/161886.html

架構演進

傳統的資料消費架構如下圖左:
1)資料來源將產生的資料寫入到訊息系統;
2)開發者藉助 Message Service 提供的 OpenAPI/SDK 或 Proxy 服務客戶端從 Message Service 讀取資料;
3)根據訊息資料處理業務邏輯,也就是我們所謂的消費訊息,將訊息消費的業務結果寫入到目標服務;如此架構開發者會面臨以下幾個問題:

  1. 如何併發安全的從 Message Service 讀取資料?
  2. 資料消費能力小於生產能力時,如何快速提升消費吞吐?
  3. 目標服務資源成為瓶頸時,如何快速擴容?當流量波峰過後,面對空閒的機器成本,您又如何處理?
  4. 如何保證消費實時性、順序性?
  5. 如何實現容錯、快取、降級、限流等高可用保護手段?
  6. 如何監控鏈路狀態或異常?
  7. ......

image.png
面對上面多個瑣碎又複雜的問題,相信總有幾個會擊中您的痛點。為了同時解決提到的所有問題,阿里雲開發 Connector Service(如上圖右)打通 Message Service 和 Serverless 計算服務的資料鏈路,您只需宣告上游的訊息服務例項和下游的消費運算元,便可一鍵部署上線,聯結器同時提供了豐富的流計算框架具備的資料處理能力和監控能力,總結如下:

  1. Transform:以 UDF 方式自定義資料清洗邏輯,同時支援 JsonPath 語法簡單提取資料;
  2. Filter:減少無用訊息的後續處理,提供多種過濾匹配規則,如:前字尾匹配、數值匹配、IP 地址匹配等;
  3. Window:提供視窗能力,可按照訊息數量和間隔時間對訊息做聚合推送。可提升訊息處理吞吐,降低訊息處理成本;
  4. Real Time:從 Message Service 拉取訊息到推送目標服務延時毫秒級別;
  5. 自定義併發消費能力:併發安全的消費訊息,提升吞吐能力;
  6. 彈性計算資源:下游計算服務根據負載自動擴縮容,無需關心伺服器資源水位問題;
  7. Monitoring + Logging + Tracing:提供了豐富的監控指標和日誌分析助力開發者監控系統狀態、定位異常;
  8. 完備的異常保障機制:自定義重試策略 + 容錯機制 + 死信佇列 + 限流 + 反壓;

為讓大家對功能有更深入的瞭解,下面我們詳細介紹各個功能的益處和應用場景。

降本提效功能

Window

在大規模資料場景中,One Message Per Request 早已無法滿足開發者需求。Window 本質是提供了一種訊息攢批處理的能力,Connector 在產品層面提供兩個可調配引數:

  • 批次推送條數:單次聚合的最大訊息條數,當積壓的訊息數量到達設定值時才會將訊息推送到下游。
  • 批次推送間隔:系統每到間隔時間點會將積壓的訊息聚合後發給下游,如果設定 0 秒錶示無等待時間,接收即投遞。

兩個引數結合使用可極大提升資料傳輸效率,進而提升資料吞吐,同時可以解鎖多種使用者場景,例:

  • 流模式實時消費:將推送間隔設為 0s,推送條數設定最大值,這樣可以保證從上游拉到的資料實時推送到下游目標服務。
  • 請求稀疏且延時不敏感場景下,希望訊息被攢批處理,可以接受消費滯後但不希望滯後時間過長:如果僅設定批次推送條數一個引數,則可能在低谷期由於訊息稀疏長時間無法達到預設的攢批條數而滯後過久,此時可引入批次推送間隔引數解決此問題。

    Transform

    訊息消費離不開資料處理,所謂資料處理,就是透過某個過程將原始資料轉為目標資料,轉換的過程即為 transform。通常原始資料是一個大而全的資訊集合,而目標資料只是一個結構化的子集,關鍵在於如何嵌入資料的清洗和提取能力。對此 Connector 提供了多種轉換能力:

  • Template:對於原資料和目標資料都是確定結構的資料,且資料提取組裝規則簡單,可以藉助模版完成 transform,模版同時支援 JsonPath 資料提取規則,如下圖:

image.png

  • UDF(User Define Function 使用者自定義函式):對原資料結構複雜,且資料轉換過程複雜的場景,可以藉助 UDF 實現。UDF 模式中,服務提供方僅約定了函式的入參協議、引數的資料結構,至於函式中如何對資料做清洗?返回的資料結構如何?全部交由開發者實現,極大提升了訊息處理的靈活度,一個簡單的 UDF demo 如下:

    # -*- coding: utf-8 -*-
    
    # handle_message 為函式執行入口
    # 服務提供方約定了入參 event 和 context 的資料格式
    # 只需從 event 中解析訊息體並做處理即可
    def handle_message(event, context):
      try:
            new_message = transform(event)
      except Exception as e:
          raise e
      return new_message
    
    def transform(old_message):
      # 自定義對資料的清洗和處理邏輯,並返回處理後的訊息
      return new_message

    Filter

    Filter 減少無用訊息的後續處理,提升訊息處理的效率,尤其和 Serverless 計算結合時,可減少呼叫次數,例如以下場景:

  • 對敏感字、非法文字、關鍵字進行過濾;
  • 對某些具有攻擊性的 IP 進行訊息攔截;
  • ......

為覆蓋足夠多的業務場景,Connector 提供了字首匹配、字尾匹配、數值匹配、IP 地址匹配等多種匹配模式,您可以根據業務需求選擇適合的模式。

Real Time

在流計算場景中,低延時消費是開發者比較關注的一個問題,Connector 在提供批處理能力的同時也兼顧了流處理場景,當時間攢批視窗設定為 0 時,系統將演變為實時消費行為。

自定義併發消費能力

以 KAFKA 為例,當 KAFKA 資料量增大時,使用者通常藉助 Topic Partition 的水平擴充套件能力提升投遞和消費的速率,隨著 Topic Partition 分割槽數的不斷增加,Consumer 端仍沿用單執行緒消費所有 partition 資料的方案一定會遇到瓶頸,進而導致訊息積壓。為了解決此問題,Connector 開放了自定義併發消費執行緒數配置,您可以指定多個 consumer threads,多個 consumer threads 會均分 kafka 的多個 partition,避免訊息積壓問題。當 Topic Partition 數量和 Consumer 執行緒數相等時可達到最大吞吐(如下圖3),同時可做到 Partition 粒度保序。

image.png

高可用保護策略

  • 重試:由於網路異常、系統 crash 等原因導致訊息消費異常時,系統會按配置的 Retry Policy 進行重試,目前支援退避重試、指數衰減重試;
  • 死信佇列:當訊息超過重試次數後仍未消費成功時,就變成了死信訊息,如果不希望死信訊息被丟棄,可以配置死信佇列,所有的死信訊息會被系統投遞到死信佇列中,目前系統支援 KAFKA、RocketMQ、MNS 作為死信佇列的目標端;
  • 容錯策略:當訊息消費發生錯誤時,系統提供以下兩種處理方式:

    • 允許容錯:允許異常容錯,當異常發生時不會阻塞執行,超出重試策略後會根據配置將訊息投遞至死信佇列或直接丟棄,繼續消費下一條訊息;
    • 禁止容錯:不允許錯誤,當異常發生並超過重試策略配置時會阻塞執行;
  • 反壓:當系統接收訊息的速率遠高於它的處理速率時,出於對系統的保護會觸發反壓機制,避免系統崩潰,反壓在系統中體現在兩方面:

    • 從上游拉訊息的速率大於下游消費速率:積壓的訊息逐漸增多,如果不控制上游的拉取速率,會導致 Connector 記憶體不足造成 OOM;
    • 下游目標服務限流:當目標服務受連線數、網路頻寬等資源限制無法服務更多請求時,會返回給 Connector 大量限流錯誤,如果 Connector 不控制訊息消費速率,可能引發系統雪崩;

      針對上面兩種場景,系統均透過技術手段做了保護,技術細節暫不描述。

    彈性計算資源

Connector 打通了訊息服務和 Serverless 函式計算服務,您可能會擔心一個問題:函式計算服務的算力能否實時適配上游訊息規模的不斷增長?答案是可以的。函式計算作為 Serverless 計算服務,底層的計算資源可以做到毫秒級伸縮,不論您的 consumer 端併發消費能力如何調整,投遞訊息的頻率有多高,函式計算均可在 quota 範圍內快速伸縮計算例項。

計算例項 Quota 是函式計算出於對業務方服務保護設定的最大併發執行例項數,如果實際業務規模大於此預設值,可以給函式計算團隊提工單調高此值。

Connector 結構

Connector 定義了資料從哪裡複製到哪裡,透過協調排程一系列 task 完成資料的傳輸工作,Task 根據職責不同可劃分為以下幾類:

  • Poller Task:從上游訊息服務中拉取訊息;
  • Transform Task:對訊息做清洗、加工、過濾、聚合等操作;
  • Sink Task:將訊息推送到下游服務;

Task 均可水平擴充套件,併發消費上游多 partition 資料,且併發將訊息投遞到下游處理。

image.png

當前 Connector 依賴阿里雲 EventBridge 實現,更多能力可參考官方文件[3]https://help.aliyun.com/product/161886.html

客戶案例

客戶需求

某廣告平臺每天將瀏覽的使用者資訊(個人資訊、時間、登入裝置等)投遞至 kafka 中,從業務角度投遞的資料格式並不完全相同,客戶需將不同格式的資料清洗為相同格式的資料,並將清洗後的資料投遞到 ClickHouse 服務,隨著使用者業務日益增長,預計未來幾個月有幾倍增長,且客戶對實時性和成本都有要求,總結客戶的幾點關鍵需求如下:

  • 具備資料清洗能力;
  • 低成本;
  • 系統不受業務增長因素影響;

    解決方案

    函式計算恰好可以完美解決上述問題,下面結合如下資料鏈路介紹如何解決客戶的幾個需求:
    image.png

  • 如何實現資料清洗?
    Transform Task 中提供了 Data Cleaning 功能,客戶可以以 UDF 方式自定義資料清洗邏輯,平臺規定了入參協議,出參可以為任意格式的清洗後資料;
  • 如何做到低成本?
    整條鏈路主要費用源於函式計算的計算資源消耗和呼叫次數,可透過以下兩個手段降低成本:

    • Window:將多條訊息聚合為一條批次訊息傳送至函式計算,減少呼叫次數,避免重複執行公共計算邏輯;
    • Filter:減少無用訊息的後續處理,減少呼叫函式計算的次數;
  • 如何保證系統不受業務增長因素影響?

透過下圖可發現,kafka topic 的 partition 分割槽數、Poller 數量、Sink Task 的 worker 數量、函式計算的計算例項數都可實現任意水平擴充套件,且均可透過配置調整,因此當客戶預判到業務增長時,只需修改相應的配置項即可實現水平擴容。

客戶業務現狀

目前客戶已將業務全量遷移到函式計算,遷移後的幾個月內僅透過簡單修改擴容配置輕鬆應對業務規模的數倍增長。

最佳實踐

下文透過演示一個將 kafka 資料匯入到函式計算的 demo,快速搭建一套訊息消費系統:

  1. 建立上游服務
    登入 kafka 控制檯 [4]建立 kakfa 例項,並在該例項下建立 topic 和 groupID,可以參考 kakfa 快速入門[5]快速完成此操作。

[5]https://help.aliyun.com/document_detail/99949.html

  1. 建立下游服務 + 配置資料處理規則

    1. 建立函式計算的服務,併為服務命名,如下圖:
      image.png
    2. 在建立的服務下建立一個函式,函式是執行程式碼的最小單元,如下圖:
      image.png
    3. 在建立函式頁面,為函式命名,並點選觸發器配置,其中觸發器型別選擇 kakfa,將 step1 建立的資源(kakfa 例項、Topic、Group ID )填寫到下圖中,其他值可使用預設值。
      image.png
    4. (可選) 如需要驗證攢批功能,可點選批次推送,並配置批次推送條數和批次推送間隔,此 demo 設定批次推送條數為 2 條,批次推送間隔為 10s,如下圖:
      image.png
    5. 上面流程完成後點選確定即部署成功。
  2. 編寫函式,函式內的邏輯為輸出接收到的訊息數量和訊息內容:

    # -*- coding: utf-8 -*-
    import logging
    import json
    
    def handler(event, context):
      evt = json.loads(event)
      logger = logging.getLogger()
      logger.info(len(evt)) // 輸出訊息列表的長度
      logger.info(evt)。    // 輸出訊息內容
      return 'succ'
  3. 測試驗證

    1. 到 kafka 控制檯的 topic 中快速傳送 3 條訊息,如下圖:
      image.png
    2. 預期函式計算會收到 2 次請求,第 1 次請求由於觸發推送條數條件包含 2 條訊息,第 2 次請求在等待 10s 後觸發推送間隔條件包含 1 條訊息,如下圖:
      image.png
    3. 可透過函式日誌檢視所有請求日誌,可以發現一共接收到 3 條訊息,如下圖:
      image.png

      總結&展望

      基於 Serverless 函式計算,您可以快速搭建一套安全可靠的資料消費系統,總結系統優勢如下:

  • 降本

    • Filter:減少無效的訊息處理和對函式計算的呼叫;
    • Window:提供訊息攢批處理能力,幫助更好處理一些非實時和離散場景下的訊息,也減少了對函式計算的呼叫次數;
    • 按需付費:計算資源按需付費的特性避免了波峰波谷場景下為峰值預留機器產生的無用開銷;
    • 持續降價:函式計算在 11 月份下調全地域全計費項價格,下調幅度達 12%-47%,並對記憶體和 cpu 做精細化計費;
  • 提效

    • 研發效率:Transform、UDF、Template、JsonPath 等能力解鎖更多業務場景,避免二次開發,助您快速構建系統,未來也會內嵌更豐富的運算元,甚至可以編排運算元;
    • 資料分析效率:提供數值檢索、視覺化分析等能力,您可以透過簡單的引導式互動,即可快速實現基於事件的流式查詢與分析;
    • 問題排查效率:系統提供豐富的可觀測能力,如事件軌跡、事件大盤等助您對業務進行監控和整體狀態分析,未來也會從指標探索、運維監控、故障定位等多個維度完善能力,實現更全面的系統可觀測性;
    • 運維效率:Serverless 計算例項毫秒級自動彈性伸縮的特性讓你徹底擺脫資源運維的負擔;

隨著雲端計算逐漸走向全面 Serverless 化,Message Service 和 Serverless 計算的連線會更加緊密,如今 Connector 的成熟更加降低了複雜系統的開發門檻,讓您真正實現端到端全鏈路深度上雲。

更多內容關注 Serverless 微信公眾號(ID:serverlessdevs),彙集 Serverless 技術最全內容,定期舉辦 Serverless 活動、直播,使用者最佳實踐。

相關文章