.NET 中的 gRPC 用戶端處理站整合

作者:James Newton-King

gRPC 與 HttpClientFactory 的整合提供建立 gRPC 用戶端的集中式方式。 其可作為設定獨立 gRPC 用戶端執行個體的替代方案。 Grpc.Net.ClientFactory NuGet 套件中提供了處理站整合。

處理站提供下列優點:

  • 提供用於設定邏輯 gRPC 用戶端執行個體的中央位置。
  • 管理基礎 HttpClientMessageHandler 的存留期。
  • 在 ASP.NET Core gRPC 服務中自動傳播期限和取消。

註冊 gRPC 用戶端

若要註冊 gRPC 用戶端,可以在 Program.cs 中應用程式進入點的 WebApplicationBuilder 執行個體內使用泛型 AddGrpcClient 擴充方法,並指定 gRPC 型別用戶端類別和服務位址:

builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

gRPC 用戶端類型會透過相依性插入 (DI) 註冊為暫時性。 您現在可以直接在 DI 所建立的類型中插入及取用用戶端。 ASP.NET Core MVC 控制器、SignalR 中樞及 gRPC 服務是可以自動插入 gRPC 用戶端的位置:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

設定 HttpHandler

HttpClientFactory 會建立 gRPC 用戶端所使用的 HttpMessageHandler。 標準 HttpClientFactory 方法可用來新增傳出要求中介軟體,或設定 HttpClient 的基礎 HttpClientHandler

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

如需詳細資訊,請參閱使用 IHttpClientFactory 發出 HTTP 要求

設定攔截器

可以使用 AddInterceptor 方法將 gRPC 攔截器新增至用戶端。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>();

上述 程式碼:

  • 註冊 GreeterClient 類型。
  • 為此用戶端設定 LoggingInterceptorLoggingInterceptor 建立一次,並在 GreeterClient 執行個體之間共用。

根據預設,攔截器會建立一次,並在用戶端之間共用。 透過在註冊攔截器時指定範圍,即可覆寫此行為。 您可以藉由指定 InterceptorScope.Client,將用戶端處理站設定為針對每個用戶端建立新的攔截器。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>(InterceptorScope.Client);

當攔截器需要來自 DI 的範圍服務或暫時性範圍服務時,建立用戶端範圍攔截器非常有用。

gRPC 攔截器或通道認證可用來隨每個要求傳送 Authorization 中繼資料。 如需設定驗證的詳細資訊,請參閱使用 gRPC 用戶端處理站傳送持有人權杖

設定通道

您可以使用 ConfigureChannel 方法,將其他組態套用至通道:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

ConfigureChannel 會傳遞 GrpcChannelOptions 執行個體。 如需詳細資訊,請參閱設定用戶端選項

注意

在執行 ConfigureChannel 回呼之前,先在 GrpcChannelOptions 上設定一些屬性:

這些值可透過 ConfigureChannel 覆寫。

呼叫認證

您可以使用 AddCallCredentials 方法將驗證標頭新增至 gRPC 呼叫:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

如需設定呼叫認證的詳細資訊,請參閱 使用 gRPC 用戶端處理站的持有人權杖

期限和取消傳播

可使用 EnableCallContextPropagation() 設定 gRPC 服務中處理站所建立的 gRPC 用戶端,以自動將期限和取消權杖傳播至子呼叫。 Grpc.AspNetCore.Server.ClientFactory NuGet 套件中提供了 EnableCallContextPropagation() 擴充方法。

呼叫內容傳播的運作方式是:從目前的 gRPC 要求內容讀取期限和取消權杖,並自動將其傳播至 gRPC 用戶端所發出的傳出呼叫。 呼叫內容傳播是確保複雜的巢狀 gRPC 案例絕對會散佈期限和取消權杖的絕佳方式。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

根據預設,如果在 gRPC 呼叫的內容之外使用用戶端,EnableCallContextPropagation 就會引發錯誤。 錯誤的設計目的是要提醒您沒有要傳播的呼叫內容。 如果您要在呼叫內容之外使用用戶端,請在使用 SuppressContextNotFoundErrors 設定用戶端時隱藏錯誤:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);

如需期限和 RPC 取消的詳細資訊,請參閱具有期限和取消功能的可靠 gRPC 服務

具名用戶端

一般而言,gRPC 用戶端類型會註冊一次,然後由 DI 直接插入到類型的建構函式。 不過,在某些情況下,為一個用戶端提供多個組態會很有用。 例如,在使用驗證和不使用驗證的情況下呼叫 gRPC 的用戶端。

透過為每個用戶端指定一個名稱,即可註冊具有相同類型的多個用戶端。 每個具名用戶端都可以有自己的組態。 泛型 AddGrpcClient 擴充方法具有包含名稱參數的多載:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

上述 程式碼:

  • 註冊 GreeterClient 類型兩次,每次註冊皆指定唯一的名稱。
  • 為每個具名用戶端設定不同的設定。 GreeterAuthenticated 註冊會將認證新增至通道,如此使用其進行的 gRPC 呼叫就會經過驗證。

具名 gRPC 用戶端會使用 GrpcClientFactory 在應用程式程式碼中建立。 所需用戶端的類型和名稱則使用泛型 GrpcClientFactory.CreateClient 方法指定:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

其他資源

gRPC 與 HttpClientFactory 的整合提供建立 gRPC 用戶端的集中式方式。 其可作為設定獨立 gRPC 用戶端執行個體的替代方案。 Grpc.Net.ClientFactory NuGet 套件中提供了處理站整合。

處理站提供下列優點:

  • 提供用於設定邏輯 gRPC 用戶端執行個體的中央位置
  • 管理基礎 HttpClientMessageHandler 的存留期
  • 在 ASP.NET Core gRPC 服務中自動傳播期限和取消

註冊 gRPC 用戶端

若要註冊 gRPC 用戶端,可以在 Startup.ConfigureServices 內使用泛型 AddGrpcClient 擴充方法,並指定 gRPC 型別用戶端類別和服務位址:

services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

gRPC 用戶端類型會透過相依性插入 (DI) 註冊為暫時性。 您現在可以直接在 DI 所建立的類型中插入及取用用戶端。 ASP.NET Core MVC 控制器、SignalR 中樞及 gRPC 服務是可以自動插入 gRPC 用戶端的位置:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

設定 HttpHandler

HttpClientFactory 會建立 gRPC 用戶端所使用的 HttpMessageHandler。 標準 HttpClientFactory 方法可用來新增傳出要求中介軟體,或設定 HttpClient 的基礎 HttpClientHandler

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

如需詳細資訊,請參閱使用 IHttpClientFactory 發出 HTTP 要求

設定攔截器

可以使用 AddInterceptor 方法將 gRPC 攔截器新增至用戶端。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>();

上述 程式碼:

  • 註冊 GreeterClient 類型。
  • 為此用戶端設定 LoggingInterceptorLoggingInterceptor 建立一次,並在 GreeterClient 執行個體之間共用。

根據預設,攔截器會建立一次,並在用戶端之間共用。 透過在註冊攔截器時指定範圍,即可覆寫此行為。 您可以藉由指定 InterceptorScope.Client,將用戶端處理站設定為針對每個用戶端建立新的攔截器。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>(InterceptorScope.Client);

當攔截器需要來自 DI 的範圍服務或暫時性範圍服務時,建立用戶端範圍攔截器非常有用。

gRPC 攔截器或通道認證可用來隨每個要求傳送 Authorization 中繼資料。 如需設定驗證的詳細資訊,請參閱使用 gRPC 用戶端處理站傳送持有人權杖

設定通道

您可以使用 ConfigureChannel 方法,將其他組態套用至通道:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

ConfigureChannel 會傳遞 GrpcChannelOptions 執行個體。 如需詳細資訊,請參閱設定用戶端選項

注意

在執行 ConfigureChannel 回呼之前,先在 GrpcChannelOptions 上設定一些屬性:

這些值可透過 ConfigureChannel 覆寫。

期限和取消傳播

可使用 EnableCallContextPropagation() 設定 gRPC 服務中處理站所建立的 gRPC 用戶端,以自動將期限和取消權杖傳播至子呼叫。 Grpc.AspNetCore.Server.ClientFactory NuGet 套件中提供了 EnableCallContextPropagation() 擴充方法。

呼叫內容傳播的運作方式是:從目前的 gRPC 要求內容讀取期限和取消權杖,並自動將其傳播至 gRPC 用戶端所發出的傳出呼叫。 呼叫內容傳播是確保複雜的巢狀 gRPC 案例絕對會散佈期限和取消權杖的絕佳方式。

services
    .AddGrpcClient<Greeter.GreeterClient>(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);

如需期限和 RPC 取消的詳細資訊,請參閱具有期限和取消功能的可靠 gRPC 服務

具名用戶端

一般而言,gRPC 用戶端類型會註冊一次,然後由 DI 直接插入到類型的建構函式。 不過,在某些情況下,為一個用戶端提供多個組態會很有用。 例如,在使用驗證和不使用驗證的情況下呼叫 gRPC 的用戶端。

透過為每個用戶端指定一個名稱,即可註冊具有相同類型的多個用戶端。 每個具名用戶端都可以有自己的組態。 泛型 AddGrpcClient 擴充方法具有包含名稱參數的多載:

services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });

services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

上述 程式碼:

  • 註冊 GreeterClient 類型兩次,每次註冊皆指定唯一的名稱。
  • 為每個具名用戶端設定不同的設定。 GreeterAuthenticated 註冊會將認證新增至通道,如此使用其進行的 gRPC 呼叫就會經過驗證。

具名 gRPC 用戶端會使用 GrpcClientFactory 在應用程式程式碼中建立。 所需用戶端的類型和名稱則使用泛型 GrpcClientFactory.CreateClient 方法指定:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

其他資源