概述
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<Greeter.GreeterClient>(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<Greeter.GreeterClient>(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服務攔截器的基類,是一個抽象類
各個方法作用如下:
方法名稱 |
作用 |
---|---|
BlockingUnaryCall |
攔截阻塞調用 |
AsyncUnaryCall |
攔截異步調用 |
AsyncServerStreamingCall |
攔截異步服務端流調用 |
AsyncClientStreamingCall |
攔截異步客戶端流調用 |
AsyncDuplexStreamingCall |
攔截異步雙向流調用 |
UnaryServerHandler |
用於攔截和傳入普通調用服務器端處理程序 |
ClientStreamingServerHandler |
用於攔截客戶端流調用的服務器端處理程序 |
ServerStreamingServerHandler |
用於攔截服務端流調用的服務器端處理程序 |
DuplexStreamingServerHandler |
用於攔截雙向流調用的服務器端處理程序 |
在實際使用中,可以根據自己的需要來使用對應的攔截方法。
本文示例為創建一個客戶端攔截器ClientLoggerInterceptor,該類繼承Interceptor。按需實現方法,這里我客戶端調用的是SayHelloAsync方法則實現對應的AsyncUnaryCall方法。
注冊攔截器:
運行效果圖:
服務器端攔截器同理,繼承Interceptor類實現對應方法。
服務器端注入方式:
services.AddGrpc(options => { options.Interceptors.Add<ServerLoggerInterceptor>(); });
調用取消
可以使用 EnableCallContextPropagation() 對 gRPC 服務中工廠所創建的 gRPC 客戶端進行配置,以自動將截止時間和取消令牌傳播到子調用。
手動傳播截止時間可能會很繁瑣。 截止時間需要傳遞給每個調用,很容易不小心錯過。 gRPC 客戶端工廠提供自動解決方案。 指定 EnableCallContextPropagation
:
- 自動將截止時間和取消令牌傳播到子調用。
- 這是確保復雜的嵌套 gRPC 場景始終傳播截止時間和取消的一種極佳方式。
services .AddGrpcClient<User.UserServiceClient>(o => { o.Address = new Uri("https://localhost:5001"); }) .EnableCallContextPropagation();
如果客戶端在 gRPC 調用的上下文之外使用,EnableCallContextPropagation
將引發錯誤。 此錯誤旨在提醒你沒有要傳播的調用上下文。 如果要在調用上下文之外使用客戶端,請使用 SuppressContextNotFoundErrors
在配置客戶端時禁止顯示該錯誤:
services .AddGrpcClient<Greeter.GreeterClient>(o => { o.Address = new Uri("https://localhost:5001"); }) .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);
配置 gRPC 重試策略
重試策略在創建 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