1. 程式人生 > 實用技巧 >Dapr微服務應用開發系列3:服務呼叫構件塊

Dapr微服務應用開發系列3:服務呼叫構件塊

題記:這篇開始逐一深入介紹各個構件塊,從服務呼叫開始

原理

所謂服務呼叫,就是通過這個構件塊讓你方便的通過HTTP或者gRPC協議同步呼叫其他服務的方法,這些方法也是通過HTTP或者gRPC來暴露的。而方便的含義在於,你無需擔心如下問題:

  • 如何發現其他服務,不用關心呼叫的鏈路以及負載均衡
  • 呼叫過程中如何保證安全性
  • 在遇到瞬態錯誤或中斷的時候如何處理重試
  • 如何記錄呼叫鏈路的跟蹤資訊

Dapr本身並沒有提供額外的API讓你去利用這些特性,上面所有的一切都通過Sidecar模式幫你橫切到Dapr邊車例項中自動處理。如下圖所示:

你的服務對其他服務發起的一切服務呼叫都要經過Dapr邊車例項,其他服務接收的一切服務呼叫同樣也要經過Dapr例項。分別執行如下步驟:

  1. 如果服務A要對服務B發起呼叫(不管HTTP還是gRPC),其實呼叫的目標是服務A的Dapr邊車;
  2. Dapr會利用Dapr的命名解析元件(後續文章會介紹)來找到服務B的的Dapr邊車位置;
  3. 然後服務A的Dapr邊車就把呼叫請求轉發給服務B的邊車了;
  4. 由於服務B的邊車和服務B是配對的,知道服務B的呼叫資訊(比如埠),所有請求被再次轉發給服務B,服務B完成服務呼叫的業務處理;
  5. 服務B處理完,把服務呼叫響應結果返回給服務B的Dapr邊車;
  6. 服務B的Dapr邊車返回響應結果給服務A的Dapr邊車;
  7. 服務A的Dapr邊車最後把響應結果返回給服務A本身。

能力

從上面的原理可以看出,通過成對的Dapr邊車,來作為服務之間呼叫的中介,就可以簡化服務和Dapr邊車之間的呼叫方式,就可以強化邊車之間的呼叫方式。

這什麼意思呢?就是Dapr把服務呼叫之間的一些共性且複雜的問題幫你解決掉(兩個邊車之間的呼叫),你只用採用最基本的HTTP和gRPC功能來暴露你的服務或者呼叫你的服務(服務與邊車之間的呼叫)。由此,你可以獲得Dapr給你提供的如下能力:

  • 定址和負載均衡:Dapr自動幫你找到要呼叫的目標服務,並自動對目標服務的多個例項進行負載均衡。
  • 名稱空間範圍限定:可以把服務放到特定的名稱空間內,從而方便隔離各類服務。這個能力最常見的用途就是用名稱空間來限定執行環境(開發、測試、生產等)。不過這個能力和託管環境有關,目前只有Kubernetes支援。
  • 重試:在分散式環境中,遠端服務出現瞬態故障是很常見的(可能由網路、負載、安全等因素造成),所以在微服務架構中針對同步服務呼叫必須實現重試機制。傳統的方式下,(就算有重試框架的幫助下)需要在業務邏輯程式碼中編寫很多冗長的重試程式碼。通過Dapr邊車內建的重試機制極度簡化了這個問題。目前Dapr的會間隔1秒最多重試3次。
  • 安全通訊:分散式環境,通訊的安全性也是一個需要重點關注的領域。Dapr提供了一個名為Sentry的基礎服務,讓邊車之間的通訊基於mTLS來進行安全保證(mTLS的證書會自動更新)。
  • 安全訪問:在安全通訊的mTLS證書的基礎,可以通過配置信任域(TrustDomain)和應用標識(App Identity)來進行訪問控制。在這裡暫時不對此話題展開。
  • 可觀測性:預設情況下,Dapr會收集邊車之間服務呼叫的度量和跟蹤資訊,從而幫助開發人員來洞察和診斷應用程式。也就是說,高大上的分散式跟蹤直接由Dapr提供內建支援了。
  • 可替換的服務發現:原理裡面提到Dapr之間的服務發現會依賴於一個稱之為命名解析元件的東西,實際上這個東西可以在不同的託管環境中進行替換。預設情況下,在Kubernetes裡面,是使用DNS Service來作為命名解析元件的實現。

規範

由於服務呼叫這個構件塊並沒有為服務應用提供什麼可直接訪問的能力,所以整個規範也相對簡單,僅僅規定了呼叫其他應用的URL模式,即通過如下地址來發送HTTP請求(或gRPC請求):

POST/GET/PUT/DELETE http://localhost:<daprPort>/v1.0/invoke/<appId>/method/<method-name>

上面的URL地址涉及到幾個約定好的引數:

  • daprPort:這是Dapr邊車暴露的HTTP埠(預設50001)或者gRPC埠(預設3500);可以通過 dapr run--dapr-grpc-port--dapr-http-port 來設定;應用內可以通過 DAPR_HTTP_PORTDAPR_GRPC_PORT 這兩個環境變數來獲得埠值。
  • appId:這是目標應用的AppId,在名稱空間(如果有)內唯一的標識;可以通過 dapr run--app-id 來設定。
  • method-name:這是需要呼叫的目標應用的介面名稱,一般是根路徑(比如 /hello )或者巢狀路徑(比如 /api/weather )也是支援的。

DOTNET SDK

作為DOTNET博主,我就僅介紹DOTNET SDK的情況。由於服務呼叫規範本身就簡單,所以SDK也相對簡單。對於被呼叫端,目前並沒有提供任何輔助的能力,你只需要使用適合的現成框架來暴露HTTP或者gRPC端點。

對於呼叫端,提供了一個客戶端類 DaprClient,有如下方法來幫助你傳送服務呼叫的請求:

  • InvokeMethodAsync
  • InvokeMethodRawAsync
  • InvokeMethodWithResponseAsync

對於DaprClient具體的用法可以參見這裡的示例程式碼:https://github.com/dapr/dotnet-sdk/blob/master/samples/Client/DaprClient/Program.cs#L217

internal static async Task InvokeDepositServiceOperationAsync(DaprClient client)
{
    Console.WriteLine("Invoking deposit");
    var data = new { id = "17", amount = (decimal)99 };

    // Invokes a POST method named "depoit" that takes input of type "Transaction" as define in the RoutingSample.
    Console.WriteLine("invoking");

    var a = await client.InvokeMethodAsync<object, Account>("routing", "deposit", data, HttpInvocationOptions.UsingPost());
    Console.WriteLine("Returned: id:{0} | Balance:{1}", a.Id, a.Balance);

    Console.WriteLine("Completed");
}

用法與例子

要了解服務呼叫構件塊具體如何使用的,照著官方文件做就是了。

對於不想看英文文件的同學,可以關注我們Dapr中文社群的翻譯過程(也歡迎加入):https://github.com/dapr-cn/docs

另外我在單獨寫一個Dapr的Quickstarts(正在逐步完善中),大家可以參考:https://github.com/heavenwing/dapr-dotnet-quickstarts/tree/main/ServiceInvocation

彩蛋——如何暴露gRPC端點

最後,官方文件裡面其實沒有把如何暴露gRPC端點這個話題講清楚,不過在SDK中其實已經把dapr的Protobuf 封裝好了(其實是自動生成好了),你引用了SDK中的Dapr.Client包就可以直接使用。我之前根據dapr的protobuf協議實現了一個例子,其實就是實現 AppCallback.AppCallbackBaseTask<InvokeResponse> OnInvoke(InvokeRequest request, ServerCallContext context) 方法,並通過ASP.NET Core來託管。 程式碼已經合併到SDK中的samples部分,見:https://github.com/dapr/dotnet-sdk/blob/master/samples/AspNetCore/GrpcServiceSample/Readme.md。呼叫程式碼見:https://github.com/dapr/dotnet-sdk/blob/master/samples/Client/DaprClient/Program.cs#L298

後來我覺得這個示例更加類似quickstarts,而不是SDK的示例,後面我會把這個示例新增到我的quickstarts中,並在SDK中去實現一個真正進行gRPC端點暴露開發的輔助能力,敬請期待。