随着以函數即服務(Function as a Service)為代表的無伺服器計算(Serverless)的廣泛使用,很多使用者遇到了涉及多個函數的場景,需要組合多個函數來共同完成一個業務目标,這正是微服務“分而治之,合而用之”的精髓所在。本文以阿裡雲 函數計算
為例,試圖全面介紹函數組合的常見模式和使用場景,希望有助于選擇合适的解決方案。
雖然本文主要介紹的是函數組合,但是基本思想也可用于服務組合。
函數同步調用函數
在這種模式裡,函數直接調用 InvokeFunction 同步 API 執行一個或者多個函數,等待被調用函數傳回結果,然後繼續執行。這是一個有些争議的模式,不使用同步調用通常有以下原因:
- 從費用的角度:由于函數計算按照函數實際執行時間收費,調用者在等待被調用函數傳回前也會産生一定費用。
- 執行時長限制:由于函數最長執行10分鐘,這就決定了調用的其它函數執行時間之和有限。
- 從容錯的角度:被調用者出錯會直接影響調用者,如果這個調用鍊很長,則這種錯誤會一直蔓延到最初的調用者,容錯性較差。同時由于執行時長限制,調用者通常不容易針對錯誤做長時間重試。
上面的理由是在有些場景下成立的,但是微服務最經典最常見的組合方式就是同步調用,函數作為微服務的一種實作方式,這種同步調用的需求是不可回避的,在有些場景下采用同步調用模式是值得考慮的,這些場景包括:
- 調用者函數需要被調用函數執行結果做後續處理或者傳回給用戶端。
- 函數執行時間較短,最好在毫秒到秒級别。
- 調用者是無狀态的,不需要針對被調用者的錯誤做複雜重試。
在這種模式裡,調用者通常不需要做複雜的計算,主要時間花在調用函數和等待傳回上,是以調用者函數可以設定較小的記憶體,以減少費用;調用者還可以根據業務需求緩存調用結果,減少對其它函數的調用,進而節約費用和增強容錯性。
函數通過 API 網關調用函數
與函數直接調用函數不同,這裡
被調用函數在 API 網關後面,使它看起來更像一個微服務。這種模式的限制和使用場景跟上面的直接調用模式類似,不同之處是 API 網關提供了一些額外的能力,比如認證和限流等。如果被調用者要根據某些業務資訊對請求做處理,則使用 API 網關是一個好的選擇。當然,使用 API 網關也帶來了額外的延遲和費用,如果不需要使用 API 網關提供的能力,則讓函數直接調用函數是一個更好的選擇。
函數異步調用函數
在這種模式裡,函數執行完自身業務邏輯後,調用 InvokeFunction 異步 API 通知其它函數執行并退出,它不關心被調用函數是否執行完成。函數計算的 InvokeFunction 異步 API 是通過隊列實作的,異步請求首先被寫入隊列,然後有一個元件從隊列裡消費請求,執行相應函數。這種模式的優勢和使用場景有:
- 對執行延遲不敏感的場景,适合削峰填谷,減輕對依賴服務的壓力。函數計算會根據使用者的資源使用情況動态的調整隊列的處理速度,平滑請求對系統的壓力,當然這種平滑也可能會導緻一些請求被延緩執行。
- 調用者可以較容易的觸發多個被調用者執行,實作 Fan-Out 模式。比如一個視訊處理函數可以異步調用多個函數來分别将視訊轉換成不同格式。
- 由于每個函數所花時間都用在執行業務邏輯,而非等待其它函數傳回上,沒有産生不必要的費用。
這種模式有如下局限性:
- 調用者顯然無法知道被調用者的執行結果。
- 被調用者之間最好是獨立執行的,不需要互相協調,否則通過資料庫來協調執行會增加複雜度。例如,上面提到的視訊處理函數調用多個函數分别将視訊轉換成不同格式,如果需要在所有的視訊轉換完成後發郵件通知使用者,這就需要等待所有的轉換函數執行結束。這種等待多個事件發生的模式叫做 Fan-In,不适合通過這種異步調用模式實作。
基于消息主題的函數調用
與上面的模式需要指定被調用函數不同,基于事件觸發模式讓調用者隻需要釋出消息,不關心誰去消費消息。在消息服務(MNS)中,主題是釋出消息的目的地,釋出者可以通過 PublishMessage API 向主題釋出消息, 主題的訂閱者會接收到釋出到主題上的消息,
MNS 與函數計算服務的內建讓函數可以直接作為訂閱者,簡化了消息處理應用的開發和運維代價。
和上面的異步調用模式相比,基于消息主題的調用模式有以下優勢:
- 進一步解耦函數調用。調用者無需知道被調用者的資訊,隻需要約定消息的格式。
- 得益于一個主題可以對應多個訂閱者,更容易實作 Fan-Out 模式。釋出者隻需要釋出一條消息就能觸發多個函數。在上面的異步調用方式中,調用者仍需要顯式調用一個或者多個函數來觸發執行。
- 雖然主題模式本質上是消息服務推送消息給訂閱者,不會考慮訂閱者的承載能力,但是由于阿裡雲消息服務是通過 InvokeFunction 異步 API 調用函數,是以這種模式也具備基于消息隊列模式的削峰填谷能力。
這種模式的局限性跟異步調用函數類似:
- 很難支援等待多個事件觸發一個函數的場景(Fan-In)。
- 需要做一定工作才能跟蹤多個函數執行狀态。
- 很難限制單個函數的執行時間或者所有函數的總執行時間。
- 無法針對函數執行錯誤定義重試政策。
基于對象存儲服務的函數調用
函數計算服務除了內建了消息主題以外,還內建了對象存儲服務 (OSS),表格存儲等其它事件源,這些事件源服務同樣可以作為連接配接函數的管道。比如,一個非函數應用對 OSS 對象操作(建立,删除等)後,通常需要其它管道(比如消息服務)通知其它應用做後續處理,而由于
OSS 和函數計算的內建,隻需簡單配置,這些事件就可以直接傳遞給自定義函數,而不需要額外管道再傳遞資訊,簡化了資料處理流水線的開發和運維代價。
基于日志庫的函數調用
日志服務的
日志庫是一個流(Stream)存儲,生産者寫入資料到日志庫,消費者讀出資料并處理。
日志服務和函數計算的內建,使得消費者可以是函數,進而實作了通過流存儲來協調函數調用。上面所有模式都是調用者顯式或者隐式的觸發一個或多個被調用函數執行,而這裡生産者函數寫入的資料不一定會立刻被消費者函數處理,日志服務會根據使用者配置将多個資料批量推送給消費者函數。
這種模式有以下優勢:
- 日志服務會針對每個分區(Shard)并行調用函數,如果資料吞吐較大,可以通過擴充分區來提高消費吞吐能力。
- 日志服務給函數推送的同一分區資料是串行的和嚴格保序的,在老的資料沒有消費成功前不會推送新的資料。
- 日志服務會持續推送相同資料到函數直到函數消費資料後傳回正常。
基于函數工作流的函數組合
函數工作流(Function Flow,簡稱 FnF)是一個用來協調多個分布式任務執行的全托管 Serverless 雲服務,簡化了開發和運作業務流程所需要的任務協調、狀态管理以及錯誤處理等繁瑣工作,讓使用者更好的專注業務邏輯開發。可以說函數工作流是轉為函數組合而生,有效的解決了上面幾種異步組合模式的局限性。
上面的所有模式都是通過點對點的方式來組合函數,而函數工作流是一個集中的協調者,函數之前不再直接或者間接通信,所有的觸發都是由函數工作流發起,不同函數的輸入和輸出是通過函數工作流來傳遞。是以,這種方式下的函數代碼全是業務邏輯相關,沒有上面模式裡的發送主題消息,或者調用其它函數的邏輯,實作更加清晰。
函數工作流有以下優勢:
- 服務編排能力:可以将流程邏輯與任務執行分開,支援多種控制原語,比如順序執行多個函數,根據函數執行結果選擇執行其它函數,讓多個函數并行處理資料,或者讓一個函數并行處理一組資料等,以及上面的 Fan-In 模式。函數工作流還内置了錯誤重試和捕獲能力,節省了編寫編排代碼的時間。
- 支援長流程:無論是毫秒級還是長達一年的業務流程,FnF 都可以跟蹤整個流程,確定流程執行完成。
- 流程狀态管理:FnF 會管理流程執行中的所有狀态,包括跟蹤它所處的執行步驟,以及存儲在步驟之間的輸入輸出。您無需自己管理流程狀态,也不必将複雜的狀态管理建構到任務中。
- 協調分布式元件:FnF 能夠協調運作在不同架構,不同網絡,不同語言實作的分布式應用。無論是私有雲、專有雲的應用想要平滑過渡到混合雲、公共雲,還是單體架構的應用想要演進到微服務架構,FnF 都能在其中發揮協調作用。函數工作流通過內建消息隊列,讓任何可以通路消息隊列的應用也可以作為流程一部分,互相協作,共同完成業務目标。
- 可視化監控:FnF 提供了可視化界面來協助定義流程和檢視執行狀态,友善您快速識别故障位置,并快速排除故障問題。
- 運維全托管和按需付費:FnF 讓您從基礎設施維護中解放出來,提供了安全的、高可用的、高容錯的彈性服務。使用者隻需支付步驟轉換費用,不使用不産生費用。
函數工作流目前還不支援同步調用方式,如果您有同步調用需求,歡迎聯系我們(見文章最後釘釘客戶群)。
總結
總的來說,上面的組合模式可以分為同步和異步兩種,在場景适合的情況下優先選擇異步模式,享受異步模式帶來的松耦合,高容錯等特性,否則使用同步模式。在異步模式中,如果需要編寫複雜的組合邏輯,支援可靠的重試,把控整個流程,則推薦使用函數工作流,否則使用消息主題或者其它事件源服務來組合函數。
上面的模式也不是割裂的,它們在有些場景下可以搭配使用,比如有時候基于對象存儲服務的調用需要觸發多個函數,這就可以
結合使用 OSS 的事件觸發和函數工作流;又比如
函數工作流通過消息隊列将任務發送給更廣泛的消費者,觸達函數計算無法觸達的地方。
最後,歡迎加入函數工作流和函數計算客戶群。
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”