消息服務 + Serverless 函數計算助力企業降本提效

背景介紹

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

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

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

名詞解釋

函數計算(Function Compute)

阿里雲函數計算是事件驅動的全託管 Serverless 計算服務。通過函數計算,您無需管理服務器等基礎設施,只需編寫代碼並上傳。函數計算會爲您準備好計算資源,以彈性、可靠的方式運行您的代碼,更多產品細節可閱讀官方文檔[1]。

連接器(Connector)

Connector 實現了大量數據的導入和導出。例如將 KAFKA topic 中數據導出到 stdout,或將本地文件中數據導入到 RocketMQ。Connector 簡化了數據在不同系統間複製和傳輸的複雜度,本文探討的消息服務和計算服務的連接同樣依賴 Connector 實現。

事件總線(EventBridge)

事件總線是 Connector 的產品化服務,支持阿里雲服務、自定義應用、SaaS 應用等以標準化、中心化的方式接入,並能夠以標準化協議在這些應用之間路由事件,幫助您輕鬆構建松耦合、分佈式的事件驅動架構,更多產品細節可閱讀官方文檔[2]。

架構演進

傳統的數據消費架構如下圖左:

1)數據源將產生的數據寫入到消息系統;

2)開發者藉助 Message Service 提供的 OpenAPI/SDK 或 Proxy 服務客戶端從 Message Service 讀取數據;

3)根據消息數據處理業務邏輯,也就是我們所謂的消費消息,將消息消費的業務結果寫入到目標服務;如此架構開發者會面臨以下幾個問題:

1、如何併發安全的從 Message Service 讀取數據?

2、數據消費能力小於生產能力時,如何快速提升消費吞吐?

3、目標服務資源成爲瓶頸時,如何快速擴容?當流量波峯過後,面對空閒的機器成本,您又如何處理?

4、如何保證消費實時性、順序性?

5、如何實現容錯、緩存、降級、限流等高可用保護手段?

6、如何監控鏈路狀態或異常?

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

Transform:以 UDF 方式自定義數據清洗邏輯,同時支持 JsonPath 語法簡單提取數據;

  1. Filter:減少無用消息的後續處理,提供多種過濾匹配規則,如:前後綴匹配、數值匹配、IP 地址匹配等;

Window:提供窗口能力,可按照消息數量和間隔時間對消息做聚合推送。可提升消息處理吞吐,降低消息處理成本;

Real Time:從 Message Service 拉取消息到推送目標服務延時毫秒級別;

自定義併發消費能力:併發安全的消費消息,提升吞吐能力;

彈性計算資源:下游計算服務根據負載自動擴縮容,無需關心服務器資源水位問題;

Monitoring + Logging + Tracing:提供了豐富的監控指標和日誌分析助力開發者監控系統狀態、定位異常;

完備的異常保障機制:自定義重試策略 + 容錯機制 + 死信隊列 + 限流 + 反壓;

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

降本提效功能

Window

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

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

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

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

Transform

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

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

  • 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 粒度保序。

高可用保護策略

  • 重試:由於網絡異常、系統 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 數據,且併發將消息投遞到下游處理。

當前 Connector 依賴阿里雲 EventBridge 實現,更多能力可參考官方文檔[3]

客戶需求

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

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

解決方案

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

  • 如何實現數據清洗?
    Transform Task 中提供了 Data Cleaning 功能,客戶可以以 UDF 方式自定義數據清洗邏輯,平臺規定了入參協議,出參可以爲任意格式的清洗後數據;
  • 如何做到低成本?
  • 整條鏈路主要費用源於函數計算的計算資源消耗和調用次數,可通過以下兩個手段降低成本:
    • Window:將多條消息聚合爲一條批量消息發送至函數計算,減少調用次數,避免重複執行公共計算邏輯;
    • Filter:減少無用消息的後續處理,減少調用函數計算的次數;
  • 如何保證系統不受業務增長因素影響?

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

客戶業務現狀

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

最佳實踐

下文通過演示一個將 kafka 數據導入到函數計算的 demo,快速搭建一套消息消費系統:

  1. 創建上游服務登錄 kafka 控制檯 [4]創建 kakfa 實例,並在該實例下創建 topic 和 groupID,可以參考 kakfa 快速入門[5]快速完成此操作。
  2. 創建下游服務 + 配置數據處理規則

a.創建函數計算的服務,併爲服務命名,如下圖:

b.在創建的服務下創建一個函數,函數是執行代碼的最小單元,如下圖:

c.在創建函數頁面,爲函數命名,並點擊觸發器配置,其中觸發器類型選擇 kakfa,將 step1 創建的資源(kakfa 實例、Topic、Group ID )填寫到下圖中,其他值可使用默認值。

d.(可選) 如需要驗證攢批功能,可點擊批量推送,並配置批量推送條數和批量推送間隔,此 demo 設置批量推送條數爲 2 條,批量推送間隔爲 10s,如下圖:

e.上面流程完成後點擊確定即部署成功。

3.編寫函數,函數內的邏輯爲輸出接收到的消息數量和消息內容:

# -*- 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'

4.測試驗證

a.到 kafka 控制檯的 topic 中快速發送 3 條消息,如下圖:

b.預期函數計算會收到 2 次請求,第 1 次請求由於觸發推送條數條件包含 2 條消息,第 2 次請求在等待 10s 後觸發推送間隔條件包含 1 條消息,如下圖:

c.可通過函數日誌查看所有請求日誌,可以發現一共接收到 3 條消息,如下圖:

總結&展望

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

  • 降本
    • Filter:減少無效的消息處理和對函數計算的調用;
    • Window:提供消息攢批處理能力,幫助更好處理一些非實時和離散場景下的消息,也減少了對函數計算的調用次數;
    • 按需付費:計算資源按需付費的特性避免了波峯波谷場景下爲峯值預留機器產生的無用開銷;
    • 持續降價:函數計算在 11 月份下調全地域全計費項價格,下調幅度達 12%-47%,並對內存和 cpu 做精細化計費;
  • 提效
    • 研發效率:Transform、UDF、Template、JsonPath 等能力解鎖更多業務場景,避免二次開發,助您快速構建系統,未來也會內嵌更豐富的算子,甚至可以編排算子;
    • 數據分析效率:提供數值檢索、可視化分析等能力,您可以通過簡單的引導式交互,即可快速實現基於事件的流式查詢與分析;
    • 問題排查效率:系統提供豐富的可觀測能力,如事件軌跡、事件大盤等助您對業務進行監控和整體狀態分析,未來也會從指標探索、運維監控、故障定位等多個維度完善能力,實現更全面的系統可觀測性;
    • 運維效率:Serverless 計算實例毫秒級自動彈性伸縮的特性讓你徹底擺脫資源運維的負擔;

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

作者 | 柳下

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

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