淺析 Dapr 里的雲計算設計模式


Dapr 實際上是把分布式系統 與微服務架構實踐的挑戰以及k8s 這三個主題的全方位的設計組合,特別是Kubernetes設計模式 一書作者Bilgin Ibryam 提出的Multi-Runtime Microservices Architecture,中譯參見敖小劍的博客: [譯] 多運行時微服務架構

分布式系統 和微服務架構實踐的核心問題就是要解決系統復雜性這個難題,降低復雜性的通常做法就是分而治之,Dapr的最核心的設計就是Sidecar Pattern + Building Block,如下圖:

Kubernetes 託管的側車架構

圖片來源:https://docs.microsoft.com/zh-cn/dotnet/architecture/dapr-for-net-developers/dapr-at-20000-feet

  • Sidecar Pattern: 通過職責分離與容器的隔離特性,降低應用程式的復雜度。
  • Building Block:  類似於樂高搭積木方法,通過Dapr 提供的核心組件(Component),分離與抽象化系統架構。

Dapr 設計上幾乎和Bilgin Ibryam 提出的Multi-Runtime Microservices Architecture 不謀而合,它有幾個核心的設計點:

  1. Sidecar
  2. Building Block & Component
  3. Service Invocation
  4. Middleware
  5. State

基於上面的這些核心設計,Dapr 有了多運行時微服務架構 的特性,以此延伸出底下的重要功能,或者說設計模式:

  1. Security
  2. Observability: tracing, metrics, logs and health
  3. Pub / Sub / Batch Process
  4. Actors
  5. Secret Management
  6. Config Management 正在開發中……

Sidecar

Sidecar是非常重要的雲計算設計模式,下面這張圖是 Sidecar 與Microservice 之間搭配后形成多個服務的關系圖,這樣的結構形成了服務網格的概念, Dapr 通過配置的方式,動態生成Sidecar ,隨后伴隨着App,一組Dapr Sidecar + App 的組合稱為Dapr App

服務網格

Dapr App 在K8s 里面的形態就是 Pod = (App_Container + Sidecar_Container)

Kubernetes 托管的 sidecar 體系結構

同樣的概念,如果Dapr App跑在k8s外面,也就是自承載模式。 在自承載模式下,微服務和 Dapr sidecar 在沒有容器業務流程協調程序(如 Kubernetes)的單獨本地進程中運行。

自承載 sidecar 體系結構

每個Dapr App 都通過Sidecar 溝通,在通信之前,Dapr App 要知道的是對方在哪?所以服務發現和服務調用是Dapr App 要解決的第一個問題,知道彼此在哪了,然后就是通信模式,Dapr 支持HTTP / gRPC 兩種通信模式。 Dapr App 之間的默認的通信模式使用gRPC,也就是如果使用HTTP調用Dapr API,內部服務之間的通信也會轉成gRPC。

gRPC 是一種新式的高性能框架,它通過 RPC (遠程過程調用) 改進。 gRPC 使用 HTTP/2 作為傳輸協議,該協議通過 HTTP RESTFul 服務提供顯著的性能增強,包括:

  • 對通過同一連接發送多個並行請求的多路復用支持 - HTTP 1.1 將處理限制為一次處理一個請求/響應消息。
  • 雙向全雙工通信,用於同時發送客戶端請求和服務器響應。
  • 內置流式處理,支持對大型數據集進行異步流式處理的請求和響應。

若要了解有關詳細信息,請查看適用於 Azure 電子書的.NET Cloud-Native中的 gRPC概述。

Dapr Sidecar 有了服務調用、服務發現和通信模式之后,定義出來了一個Building Block (構建塊)的概念,使用聲明的方式,定義多個組件Component 擴展Sidecar的能力,這些能力正是分布式系統需要面對的問題。

構建塊 和 組件

構建基塊封裝分布式基礎結構功能。 可以通過 HTTP 或 gRPC API 訪問該功能,目前版本有如下構建塊。

Dapr 構建基塊

Buiding Block 是每個 Dapr Sidecar 可以擴展的概念,每個 Block 由多個 Components 組成,開發者可以自行設計、擴展 Component,然后貢獻給社區,這里集中了社區貢獻的組件 https://github.com/dapr/components-contrib。 我們來看一下微軟的.NET團隊基於Dapr 設計的eshopondapr,圖中每個Dapr標示都是一個Component ,一共標記了六種:

eShopOnDapr reference application architecture

基於這樣的設計,Dapr 把最核心的Component 提供了基於分布式系統的 最佳實踐 (Best Practice)和 設計模式 (Design Patterns)

  1. Input/Output Bindings:
  2. Pub / Sub:
  3. Service discovery name resolution: Dapr 的特殊 Components,后面介紹。
  4. State Stores
  5. Secret Stores

這些核心的設計可以通過代碼倉庫了解:

image

倉庫 https://github.com/dapr/components-contrib 是Dapr 官方開放的Component ,開發者可以通過 PR 提交來把 擴展的Component 貢獻給社區,目前已經有70 多個Components 可以使用,使用的時候要注意版本的階段性是在Alpha / Beta / GA,一定要做好風險評估。

服務調用和服務發現

這就是我們在微服務里面常說的服務治理,Dapr 作為一個分布式系統,多個Dapr app怎么知道彼此的存在,通過什么方式進行溝通,這就是Dapr的服務治理要解決的問題,Dapr的服務發現機制,按照架構的不同方式(k8s還是自托管)有不同的實現,官方文檔(https://docs.dapr.io/zh-hans/developing-applications/building-blocks/service-invocation/service-invocation-overview/)里的這張圖介紹了調用邏輯

Diagram showing the steps of service invocation

  1. 服務 A 對服務 B 發起HTTP/gRPC的調用。

  2. Dapr 使用 name resolution component 發現 Service B’s 位置 取決於運行的環境 hosting platform.

  3. Dapr 將消息轉發至服務 B的 Dapr 邊車

    : Dapr 邊車之間的所有調用考慮到性能都優先使用 gRPC。 僅服務與 Dapr 邊車之間的調用可以是 HTTP 或 gRPC

     

  4. 服務 B的 Dapr 邊車將請求轉發至服務 B 上的特定端點 (或方法) 。 服務 B 隨后運行其業務邏輯代碼。

  5. 服務 B 發送響應給服務 A。 響應將轉至服務 B 的邊車。

  6. Dapr 將消息轉發至服務 A 的 Dapr 邊車。

  7. 服務 A 接收響應。

這里面有很多核心的概念:

  • Dapr 命名有Namespace 概念,基本格式為FQDN
  • Dapr 的Service Invocation 支持 gRPC / HTTP 兩種方式,默認的 Service to Service 使用gRPC 通信。
  • Service to Service 使用mTLS 做傳輸層加密
  • 支持 Service 的ACL,可以個別控制每個API 與Method 的操作
  • 支持 Retry 機制
  • 可以使用其他service discovery 實現
  • RR load balancing with mDNS
  • 支持tracing 和 metric

Middleware

和ASP.NET Core 支持通過 middleware 處理 HTTP request / response 完成一些 Cross-Cutting (AoP) 的功能,Dapr 也支持 Middleware 的概念,如下圖:

image

Dapr 允許通過鏈接一系列中間件組件來定義自定義處理管道。 請求在路由到用戶代碼之前經過所有已定義的中間件組件,然后在返回到客戶機之前,按相反順序經過已定義的中間件,如下圖中所示。

Actors

Actor 模型 起源於Carl Hewitt  在 1973 年提出的作為並發計算的概念模型,這種形式的計算會同時執行多個計算。 當時並沒有高度並行的計算機,但多核 Cpu 和分布式系統的最新進步使得Actor 模型 變得流行。在Actor 模型中,Actor 是一個計算和狀態獨立的單元。 Actors 完全彼此隔離,它們永遠不會共享內存。 Actors 使用消息相互通信。 當一個Actor 收到消息時,它可以更改其內部狀態,並將消息發送到其他 (可能是新的) Actors。

Actor模型使得編寫並發系統變得更簡單的,它提供了基於 turn-based 的 (或單線程) 訪問模型。 多個Actors可以同時運行,但每個Actor 一次只處理一個接收的消息。 這意味着,在任何時候,都可以確保在Actors 中最多有一個線程處於活動狀態。 這使得編寫正確的並發系統和並行系統變得更加容易。

Dapr 的實現基於 項目 "Orleans" 中引入的虛擬Actor模式。 對於虛擬Actor模式,不需要顯式的創建Actor。 第一次將消息發送到Actor時,Actor將被隱式激活並放置在群集中的節點上。 當不執行操作時,Actor 會以靜默方式從內存中卸載。 如果某個節點出現故障,Dapr 會自動將激活的Actor 移到正常的節點。 除了在Actor之間發送消息以外,Dapr Actor模型還支持使用計時器和提醒調度將來的工作。

雖然Actor模型 提供了很大的優勢,但必須仔細考慮Actor的設計。 例如,如果多個客戶端調用相同的Actor,則會導致性能不佳,因為Actor  操作會按順序執行。 下面的檢查清單是是否適用於 Dapr Actor的一些標准:

  • 問題空間涉及並發性。 如果沒有Actor,則需要在代碼中引入顯式鎖定機制。
  • 可以將問題空間分區為小、獨立和隔離的狀態和邏輯單元。
  • 不需要低延遲的讀取Actor 狀態。  因為Actor 操作是按順序執行,不能保證低延遲讀取。
  • 不需要在一組Actor 之間查詢狀態。 跨Actor 的查詢效率低下,因為每個Actor 的狀態都需要單獨讀取,並且可能會導致不可預測的延遲。

滿足這些條件的一種設計模式非常好,就是 基於業務流程的 saga流程管理器 設計模式。 Saga 管理必須執行的一系列步驟才能達到某些結果。 Saga (或進程管理器) 維護序列的當前狀態,並觸發下一步。 如果一個步驟失敗,saga 可以執行補償操作。 利用Actor,可以輕松處理 saga 中的並發,並跟蹤當前狀態。 EShopOnDapr 參考應用程序使用 saga 模式和 Dapr Actor來實現排序過程。

執行組件放置服務的關系圖。

為了提供可伸縮性和可靠性,將在Actor服務的所有實例中對actor進行分區。 Dapr placement  服務負責跟蹤分區信息。 啟動Actor 服務的新實例時,Sidecar 會將支持的Actor 類型注冊到placement 服務。 placement 服務計算給定Actor 類型的更新分區信息,並將其廣播給所有實例。

關於 Actor Pattern 的信息以及相關論文:

總結

分布式架構的門檻比較高,需要考慮的問題很多,通常我們都需要考慮如下問題。

  1. 服務治理:包含Service Invocation、Service Trusted and Authorization (服務的信任、認證與授權)、通信模式(HTTP / gRPC)、通信機制(Push / Pull)、狀態管理(State Machine)
  2. 運維:高可用、擴展機制、Log 處理、分布式追蹤、Metric
  3. 安全性:Data Encryption、Secret Management、KMS、Auth 集成
  4. 性能和可靠性:Rate Limit、降級、熔斷…
  5. 可擴展的架構
  6. 如何提升開發團隊的效能

上述的這些東西,通常是一個有經驗的、資深的軟件工程師,如何在資源有限的情況下,可以快速開發、容易測試,是很多技術人的痛點所在。

這些問題從個別來看,都有相當成熟的系統,如果個別看,有很多現成的實踐可以參考。但是對於存在了幾十年的祖傳代碼的系統架構而言,如果要進行微服務改造,往往都要傷筋動骨,讓技術主管和架構師傷透腦筋,往往要面對新舊技術的整合,同時也要面對現實的團隊需求的交互和對於新技術的學習門檻。對於開發應用程式的開發人員來講,滿足業務需求的開發已經夠頭痛了,還要考慮這么多系統架構層面的東西,這種事是無法靠熱情填補的。Dapr 將一些經過驗證的技術和最佳實踐帶到微服務開發中。它通過即插即用模型將90 年代的數據驅動的客戶端/服務器應用程序的操作,應用於現代雲原生應用程序所需的最常見服務,讓我們集中於業務需求的開發,而不需要考慮系統架構層面的東西 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM