.Net Core gRPC 實戰(二)


概述

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

 


免責聲明!

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



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