.Net Core gRPC 實戰(二)

作者: Stacking

原文連結: https://www。cnblogs。com/Stacking/p/net-core-grpc-2。html

概述

gRPC 客戶端必須使用與服務相同的連線級別安全性。 如呼叫服務時通道和服務的連線級別安全性不一致,gRPC 客戶端就會丟擲錯誤。

gRPC 配置使用HTTP

gRPC 客戶端傳輸層安全性 (TLS) 是在建立 gRPC 通道時伺服器地址以https開頭配置的。若要配置為http協議做如下修改

AppContext。SetSwitch(“System。Net。Http。SocketsHttpHandler。Http2UnencryptedSupport”, true);GrpcChannel。ForAddress(“http://localhost:5000”)

關於通道和客戶端的說明

建立通道成本高昂。 重用 gRPC 呼叫的通道可提高效能。

gRPC 客戶端是使用通道建立的。 gRPC 客戶端是輕型物件,無需快取或重用。

配置截止時間

建議配置 gRPC 呼叫的截止時間,因為它限制呼叫時間的上限,阻止異常執行的服務持續執行並耗盡伺服器資源。 截止時間對於構建可靠應用非常有效。

進行呼叫時,使用

CallOptions。Deadline

配置截止時間。

如果超過了截止時間,客戶端和服務將有不同的行為:

客戶端將立即中止基礎的 HTTP 請求並引發

DeadlineExceeded

錯誤。 客戶端可以選擇捕獲錯誤並向用戶顯示超時訊息。

伺服器將中止正在執行的 HTTP 請求,並引發 ServerCallContext。CancellationToken。 儘管中止了 HTTP 請求,gRPC 呼叫仍將繼續執行直到方法完成。 將取消令牌傳遞給非同步方法,使其隨呼叫一同被取消。 例如,向非同步資料庫查詢和 HTTP 請求傳遞取消令牌。 傳遞取消令牌讓取消的呼叫可以在伺服器上快速完成,併為其他呼叫釋放資源。

客戶端程式碼示例:

try{ var response = await client。SayHelloAsync( new HelloRequest { Name = “World” }, deadline: DateTime。UtcNow。AddSeconds(5)); // Greeting: Hello World Console。WriteLine(“Greeting: ” + response。Message);}catch (RpcException ex) when (ex。StatusCode == StatusCode。DeadlineExceeded){ Console。WriteLine(“Greeting timeout。”);}

伺服器程式碼示例:

var response = await client。GetUserAsync( new UserRequest { Id = request。Id }, deadline: context。Deadline);

註冊 gRPC 客戶端

在 Startup類的ConfigureServices方法中,使用 AddGrpcClient 擴充套件方法指定 gRPC客戶端類和服務地址。

services。AddGrpcClient(o =>{ o。Address = new Uri(“https://localhost:5001”);});

ASP。NET Core MVC 控制器和 gRPC 服務等透過建構函式等方式自動注入。

配置 HttpHandler

。ConfigurePrimaryHttpMessageHandler(() => new GrpcWebHandler(GrpcWebMode。GrpcWebText, new HttpClientHandler()));

配置通道和攔截器

通道

通道(Channel)是。Net Core 3。X引入的型別,Channel是執行緒安全的,Channel的預期用例是多執行緒場景,可以實現多執行緒之間通訊。類似Golang的chan型別。

通道相關文章:https://webmote。blog。csdn。net/article/details/115361367

gRPC配置通道示例:

services 。AddGrpcClient(o => { o。Address = new Uri(“https://localhost:5001”); }) 。ConfigureChannel(o => { var credentials = CallCredentials。FromInterceptor((context, metadata) => { if (!string。IsNullOrEmpty(_token)) { metadata。Add(“Authorization”, $“Bearer {_token}”); } return Task。CompletedTask; }); o。Credentials = ChannelCredentials。Create(new SslCredentials(), credentials); });

攔截器

具有面向切面的思想,可以在呼叫服務的時候進行一些統一處理, 很適合在這裡處理驗證、日誌等流程。

Interceptor類是gRPC服務攔截器的基類,是一個抽象類

.Net Core gRPC 實戰(二)

各個方法作用如下:

方法名稱

作用

BlockingUnaryCall

攔截阻塞呼叫

AsyncUnaryCall

攔截非同步呼叫

AsyncServerStreamingCall

攔截非同步服務端流呼叫

AsyncClientStreamingCall

攔截非同步客戶端流呼叫

AsyncDuplexStreamingCall

攔截非同步雙向流呼叫

UnaryServerHandler

用於攔截和傳入普通呼叫伺服器端處理程式

ClientStreamingServerHandler

用於攔截客戶端流呼叫的伺服器端處理程式

ServerStreamingServerHandler

用於攔截服務端流呼叫的伺服器端處理程式

DuplexStreamingServerHandler

用於攔截雙向流呼叫的伺服器端處理程式

在實際使用中,可以根據自己的需要來使用對應的攔截方法。

本文示例為建立一個客戶端攔截器ClientLoggerInterceptor,該類繼承Interceptor。按需實現方法,這裡我客戶端呼叫的是SayHelloAsync方法則實現對應的AsyncUnaryCall方法。

.Net Core gRPC 實戰(二)

註冊攔截器:

.Net Core gRPC 實戰(二)

執行效果圖:

.Net Core gRPC 實戰(二)

伺服器端攔截器同理,繼承Interceptor類實現對應方法。

伺服器端注入方式:

services。AddGrpc(options => { options。Interceptors。Add(); });

呼叫取消

可以使用 EnableCallContextPropagation() 對 gRPC 服務中工廠所建立的 gRPC 客戶端進行配置,以自動將截止時間和取消令牌傳播到子呼叫。

手動傳播截止時間可能會很繁瑣。 截止時間需要傳遞給每個呼叫,很容易不小心錯過。 gRPC 客戶端工廠提供自動解決方案。 指定

EnableCallContextPropagation

自動將截止時間和取消令牌傳播到子呼叫。

這是確保複雜的巢狀 gRPC 場景始終傳播截止時間和取消的一種極佳方式。

services 。AddGrpcClient(o => { o。Address = new Uri(“https://localhost:5001”); }) 。EnableCallContextPropagation();

如果客戶端在 gRPC 呼叫的上下文之外使用,

EnableCallContextPropagation

將引發錯誤。 此錯誤旨在提醒你沒有要傳播的呼叫上下文。 如果要在呼叫上下文之外使用客戶端,請使用

SuppressContextNotFoundErrors

在配置客戶端時禁止顯示該錯誤:

services 。AddGrpcClient(o => { o。Address = new Uri(“https://localhost:5001”); }) 。EnableCallContextPropagation(o => o。SuppressContextNotFoundErrors = true);

重試策略在建立 gRPC 通道時配置

var defaultMethodConfig = new MethodConfig { Names = { MethodName。Default }, RetryPolicy = new RetryPolicy { MaxAttempts = 5, InitialBackoff = TimeSpan。FromSeconds(1), MaxBackoff = TimeSpan。FromSeconds(5), BackoffMultiplier = 1。5, RetryableStatusCodes = { StatusCode。Unavailable } } }; var channel = GrpcChannel。ForAddress(“http://localhost:5000”, new GrpcChannelOptions { LoggerFactory = loggerFactory, ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } } });

重試策略可以按方法配置,而方法可以使用 Names 屬性進行匹配。 此方法配置有 MethodName。Default,因此它將應用於此通道呼叫的所有 gRPC 方法。

gRPC 重試選項

下表描述了用於配置 gRPC 重試策略的選項:

選項

描述

MaxAttempts

最大呼叫嘗試次數,包括原始嘗試。 此值受

GrpcChannelOptions。MaxRetryAttempts

(預設值為 5)的限制。 必須為該選項提供值,且值必須大於 1。

InitialBackoff

重試嘗試之間的初始退避延遲。 介於 0 與當前退避之間的隨機延遲確定何時進行下一次重試嘗試。 每次嘗試後,當前退避將乘以

BackoffMultiplier

。 必須為該選項提供值,且值必須大於 0。

MaxBackoff

最大退避會限制指數退避增長的上限。 必須為該選項提供值,且值必須大於 0。

BackoffMultiplier

每次重試嘗試後,退避將乘以該值,並將在乘數大於 1 的情況下以指數方式增加。 必須為該選項提供值,且值必須大於 0。

RetryableStatusCodes

狀態程式碼的集合。 具有匹配狀態的失敗 gRPC 呼叫將自動重試。 有關狀態程式碼的更多資訊,請參閱狀態程式碼及其在 gRPC 中的用法。 至少需要提供一個可重試的狀態程式碼。

配置 gRPC hedging 策略

Hedging 是一種備選重試策略。 Hedged gRPC 呼叫可以在伺服器上執行多次,並獲取第一個成功的結果。

重要的是,務必僅針對可安全執行多次且不會造成負面影響的方法啟用 hedging。且hedging 策略不能與重試策略結合使用。

Hedging 具有以下優缺點:

Hedging 的優點是,它可能更快地返回成功的結果。 它允許同時進行多個 gRPC 呼叫,並在出現第一個成功的結果時完成。

Hedging 的一個缺點是它可能會造成浪費。 進行了多個呼叫並且這些呼叫全部成功。 僅使用第一個結果放棄其餘結果。

Hedging 策略的配置類似於重試策略:

var defaultMethodConfig = new MethodConfig { Names = { MethodName。Default }, HedgingPolicy = new HedgingPolicy { MaxAttempts = 5, NonFatalStatusCodes = { StatusCode。Unavailable } } }; var channel = GrpcChannel。ForAddress(“http://localhost:5000”, new GrpcChannelOptions { LoggerFactory = loggerFactory, ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } } });

gRPC hedging 選項

下表描述了用於配置 gRPC hedging 策略的選項:

選項

描述

MaxAttempts

Hedging 策略將傳送的呼叫數量上限。

MaxAttempts

表示所有嘗試的總數,包括原始嘗試。 此值受

GrpcChannelOptions。MaxRetryAttempts

(預設值為 5)的限制。 必須為該選項提供值,且值必須大於 2。

HedgingDelay

第一次呼叫立即傳送,但後續 hedging 呼叫將按該值延遲傳送。 如果延遲設定為零或

null

,那麼所有所有 hedged 呼叫都將立即傳送。 預設值為 0。

NonFatalStatusCodes

指示其他 hedge 呼叫仍可能會成功的狀態程式碼集合。 如果伺服器返回非致命狀態程式碼,hedged 呼叫將繼續。 否則,將取消未完成的請求,並將錯誤返回到應用。 有關狀態程式碼的更多資訊,請參閱狀態程式碼及其在 gRPC 中的用法。

Github

本文示例程式碼:https://github。com/MayueCif/GrpcDemo