Serverless 常見的應用設計模式

引言

2014 年我們發佈了 Lambda 服務,掀起了 Serverless 革命。現在越來越多的人談論 Serverless 的未來。事實上,我們自己構建的應用程序中有一半以上是基於 Lambda 的,Serverless 能夠最大限度地利用雲計算的價值。現在,越來越多的客戶正在決定採用 Serverless。這裏,我們不只是在談論 Lambda、API Gateway、Step Functions 或 EventBridge 等 Serverless 服務,而是如何使用 Serverless 實現快速原型設計、成本可控、高可用、自動擴展以及高效運維,這些都是用戶在選擇初始應用架構時需要考慮的關鍵設計因素。

Serverless 是試驗、學習和超越競爭對手的偉大推動力。

在應用設計領域,設計模式是架構的基石,每種設計模式都來自一個反覆出現的常見架構問題,通過總結該問題的解決方案,最終形成可複用的模式。這樣,來自五湖四海的架構師們,就能根據這些設計模式,站在前人的經驗之上,針對現實問題,明智地選擇滿足要求的架構設計。本文,我們將嘗試總結一些有關 Serverless 常見的應用設計模式。

反模式示例

在逐個分析 Serverless 應用設計模式之前,我們可以先聊聊那些 “反模式”,“不是什麼” 比“是什麼”更容易掌握。

1、Lambda 函數成單體

這種使用方式在用戶中相當常見,talk is cheap, show me the code,寫一個臃腫的 Lambda 函數,裏面包含了各種事件觸發所需的處理邏輯,從零開始的效率很高,隨着複雜性的增加,這會導致 Lambda 函數的代碼包變大,冷啓動時間變長,運行速度變慢,函數的 IAM 角色必須授予所有資源的權限,違反了最小權限原則,對該 Lambda 函數所需依賴的升級更具風險,不同的開發者需協作維護,測試覆蓋率難以提升,團隊擴展也受到影響。單任務的 Lambda 函數邏輯是定義拆解邊界的起點,未來我們會來探討將事件風暴的思路應用到 Serverless 設計中。

2、Lambda 函數成編排器

複雜的工作流邏輯是現實應用的真實反映,在 Lambda 函數中實現整個工作流,會導致代碼難以閱讀、理解和維護,而且必須細心處理錯誤和重試邏輯,這使得複雜性成倍提升,質量保障難度增加。使用 Step Functions 服務,利用版本化的 JSON 定義狀態機,對所需的工作流程進行編排纔是合理的解決之道。在狀態機中可以處理嵌套的工作流邏輯、錯誤和重試。不同版本的工作流,可以很方便對生產系統進行升級或回滾,此外還可以減少自定義代碼,使應用程序更易於測試和維護。雖然 Step Functions 最適合界限上下文的工作流,但爲了協調多服務之間的狀態更改,請改爲使用 EventBridge,利用事件總線,根據路由規則簡化編排。

3、Lambda 調用 Lambda

大多數編程語言都支持在代碼中同步調用函數的方法。在這種情況下,調用者會一直等待,直到函數返回響應。這是一種反模式。首先成本考慮,Lambda 服務是按調用時間進行付費,這種模式不符合成本可控原則。其次,在嵌套調用中,錯誤處理會變得更加複雜,水桶效應,即最慢的功能影響了整個工作流的效率。再次,調用者與被調函數的併發性有共生關係,而併發性在繁忙的系統中容易造成性能瓶頸。

有兩種方法可以避免這種模式。一種是在 Lambda 函數之間使用 SQS 隊列,解耦這兩個功能。第二種是使用 Step Functions,可以幫助減少編排工作流所需的自定義代碼,着重在錯誤和重試處理,而 Lambda 函數僅包含業務邏輯即可。

4、事件死循環

Lambda 函數是事件驅動的,Lambda 函數本身也可以產生新的事件,所以這中間處理不善可能引起事件死循環。雖然大多數編程語言都存在無限循環的可能性,但這種反模式在 Serverless 中會消耗更多資源,主要的原因就在於支持針對流量的自動擴展,事件循環會導致 Lambda 的併發擴展,Lambda 的併發擴展會生成更多事件。在這種情況下,可以手動在 Lambda 控制檯中使用 “Throttle” 按鈕,將函數併發縮減爲零以打破死循環。建議使用正向觸發器,保留併發,利用 CloudWatch 監控和警報。

更多詳細內容,可參考 James Beswick 的文章《Operating Lambda: Anti-patterns in event-driven architectures – Part 3》。

常見的設計模式

當前,我們正在構建越來越複雜的平臺,同時也努力解決不斷變化的業務需求,並按時交付給越來越多的用戶。持續快速交付優質軟件是用戶的核心業務優勢。使用現代架構、框架和實踐加速開發過程具有戰略意義。Serverless 非常適合實現快速、持續的軟件交付,無需考慮管理基礎架構、配置或規劃需求和規模,將代碼構建爲更小、更簡單的單元,這些單元易於理解、更改和部署到生產環境,使我們能夠交付業務價值並快速迭代。設計模式是推廣最佳實踐和共享解決方案的有力武器,預見可行經過驗證的 Serverless 設計模式來解決現代雲架構中的常見需要。Peter Sbarski 在他的《Serverless Architectures on AWS》一書中列出了 Serverless 架構中常見的五種設計模式,當然這些並不是一個詳盡的集合。

 Serverless Land 網站,推出了更多的模式集合,並提供了 Serverless 模版示例代碼。可以選擇合適的服務,生成 SAM 模板複製粘貼到您的代碼中最難過。我們將繼續添加新的模式,並接受社區的貢獻來持續完善這個模式集合,詳細可參考這裏:

http://serverlessland.com/patterns/

1、命令模式 

在軟件工程中,命令模式是一種行爲設計模式,將請求封裝爲包含該請求所有信息的獨立對象,允許將請求作爲方法參數傳遞、延遲或排隊請求的執行,並支持可撤消的操作。命令模式允許將操作的調用者與執行所需處理的實體分離。

在實踐中,這種模式可以簡化 API 網關的實現,因爲不希望或不需要爲每種類型的請求創建一個 REST API,還可以使版本控制變得更加簡單。Lambda 函數(命令)可以與不同版本的客戶端一起使用,並調用客戶端所需的不同服務。該模式可解耦調用者和接收者,將參數作爲對象傳遞,並允許客戶端使用不同的請求進行參數化,以減少組件之間的耦合,有助於系統的可擴展性。

下圖就是一個很好的例子,該服務集中了客戶端的請求,以減少通信開銷的影響,並向下遊服務發出分解的請求,在響應到達時收集、存儲和聚合響應,作爲一個響應,返回給調用者。

2、消息傳遞模式

異步消息傳遞是大多數服務集成的基礎,已被證明是企業架構的最佳策略,允許構建松耦合的架構,以克服遠程服務通信的限制,如延遲和不可靠性。

下圖所示的消息傳遞模式在分佈式系統中很流行,允許開發者從彼此的直接依賴中解耦出來,並允許將事件 / 記錄 / 請求存儲在隊列中,構建可擴展且健壯的系統。如果消費者下線,消息將保留在隊列中,仍然可以等消費者恢復後繼續處理。

一個消息隊列的例子,其中包含,一個發送者可以發佈到隊列,一個接收者可以從隊列中檢索消息。實施方面,可以使用 SQS 構建此模式。

消息隊列包含多個發送方 / 接收方的時候,而每個 SQS 隊列通常只有一個接收器。如果需要有多個消費者,一個直接的方法是在系統中引入多個隊列,可以將 SQS 與 SNS 結合使用。SQS 隊列可以訂閱一個 SNS 主題,將消息推送到 SNS 主題,SQS 會自動將消息推送到所有訂閱的隊列。

Kinesis Streams 是 SQS 的替代品,儘管它沒有某些功能,例如消息的死信。Kinesis Streams 與 Lambda 集成,提供有序的記錄序列,並支持多個使用者。

這是一種用於處理工作負載和數據處理的流行模式。隊列用作緩衝區,因此如果消費者崩潰,數據不會丟失,仍將保留在隊列中,直到消費者恢復並再次開始處理。消息隊列也可以使未來的更改更容易,因爲函數之間的耦合更少。在具有大量數據處理、消息和請求的環境中,儘量減少直接依賴於其他函數,可改用消息傳遞模式。

3、優先隊列模式

使用 Serverless 架構的一大好處是容量規劃和可擴展性,但在某些情況下,希望控制系統處理消息的方式和時間,例如將不同的隊列、主題或流來將消息提供給函數。

這也就意味着,對於不同優先級的消息擁有完全不同的工作流。優先級高的消息,會通過使用更昂貴的服務和容量更大的 API 來加快工作流,而不需要儘快處理的消息則使用不同的工作流。

此模式涉及創建和使用完全不同的 SNS 主題、Kinesis Streams、SQS 隊列、Lambda 函數,甚至第三方服務。當需要處理具有不同優先級的消息時,此模式適用,可以通過不同工作流的實現,構建不同的服務和 API,滿足多種類型的用戶需求。

4、扇出模式

扇出是許多用戶熟悉的一種消息傳遞模式。通常,扇出模式用於將消息推送到特定隊列或消息管道訂閱的所有客戶端。 

此模式通常使用 SNS 主題實現,當向主題添加新消息時,允許調用多個訂閱者。以 S3 爲例。將新文件添加到存儲桶時,S3 可以使用文件的消息,調用單個 Lambda 函數。

但如果需要同時調用兩個、三個或更多 Lambda 函數怎麼辦?並行執行更多的 Lambda 函數,答案是使用 SNS 的扇出模式。

SNS 主題是可以有多個發佈者和訂閱者(包括 Lambda 函數)的消息傳遞渠道。當新消息添加到主題時,會強制並行調用所有訂閱者,從而導致事件扇出。

回到前面討論的 S3 示例,可以將 S3 配置爲將消息推送到 SNS 主題,同時調用所有訂閱的函數,而不是調用單個 Lambda 函數。這是創建事件驅動架構和並行執行操作的有效方法。

同時調用多個 Lambda 函數,此模式很適用。如果 SNS 主題無法傳遞消息或函數無法執行,將嘗試並重試調用 Lambda 函數。

此外,扇出模式不僅可以用於調用多個 Lambda 函數。SNS 主題支持其他訂閱者,例如電子郵件和 SQS 隊列。向主題添加新消息可以同時調用 Lambda 函數、發送電子郵件或將消息推送到 SQS 隊列。

5、管道和過濾器模式

管道和過濾器模式的目的是將複雜的處理任務分解爲一系列在管道中可管理、分散的服務。用於轉換數據的組件,傳統上稱爲過濾器,而將數據從一個組件傳遞到下一個組件的連接器,稱爲管道。Serverless 架構非常適合這種模式,特別是對於需要多個步驟纔有結果的任務類型,非常有用。

建議將每個 Lambda 函數編寫爲細粒度的任務,並牢記單一任務原則。輸入和輸出應該明確定義。

每當有一項複雜的任務時,請嘗試將其分解爲一系列管道,並應用以下規則:

總結

本文重點介紹了 Serverless 的反模式和常見的設計模式,在用戶開始構建初始架構之前,瞭解和考慮這些至關重要。我們討論的內容包括以下反模式:

  1. Lambda 函數成單體

  2. Lambda 函數成編排器

  3. Lambda 調用 Lambda

  4. 事件死循環

同時,我們也介紹了以下這些 Serverless 常見的設計模式:

  1. 命令模式

  2. 消息傳遞模式

  3. 優先隊列模式

  4. 扇出模式

  5. 管道和過濾器模式

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kgz778rs4jOwqvJCktFzeg