.NET 中的 gRPC 客户端工厂集成
注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
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
类型。 - 为此客户端配置
LoggingInterceptor
。LoggingInterceptor
创建一次,并在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
上设置了一些属性:
HttpHandler
设置为 ConfigurePrimaryHttpMessageHandler 的结果。LoggerFactory
设置为从 DI 解析的 ILoggerFactory。
这些值可以通过 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 调用就经过了身份验证。
使用 GrpcClientFactory
在应用代码中创建一个命名的 gRPC 客户端。 所需客户端的类型和名称是使用泛型 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
类型。 - 为此客户端配置
LoggingInterceptor
。LoggingInterceptor
创建一次,并在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
上设置了一些属性:
HttpHandler
设置为 ConfigurePrimaryHttpMessageHandler 的结果。LoggerFactory
设置为从 DI 解析的 ILoggerFactory。
这些值可以通过 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 调用就经过了身份验证。
使用 GrpcClientFactory
在应用代码中创建一个命名的 gRPC 客户端。 所需客户端的类型和名称是使用泛型 GrpcClientFactory.CreateClient
方法指定的:
public class AggregatorService : Aggregator.AggregatorBase
{
private readonly Greeter.GreeterClient _client;
public AggregatorService(GrpcClientFactory grpcClientFactory)
{
_client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
}
}