在分布式服务架构中,gRPC服务通常会部署多个实例来提升整体吞吐量和可用性,.NET应用作为gRPC客户端时,通过客户端负载均衡可以将请求合理分配到不同的服务实例,避免单实例过载。结合.NET内置的gRPC客户端工厂和负载均衡组件,不需要引入额外复杂依赖就能实现这个功能。

.NET gRPC客户端工厂基础用法
.NET提供了Grpc.Net.ClientFactory包来集成gRPC客户端,通过依赖注入的方式管理客户端生命周期,避免重复创建连接带来的性能损耗。首先需要安装对应的NuGet包:
<!-- 项目文件中的包引用 --> <PackageReference Include="Grpc.Net.ClientFactory" Version="2.55.0" /> <PackageReference Include="Google.Protobuf" Version="3.24.0" /> <PackageReference Include="Grpc.Tools" Version="2.56.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference>
在Program.cs中注册gRPC客户端,基础注册方式如下:
var builder = WebApplication.CreateBuilder(args);
// 注册gRPC客户端,GreeterClient是proto文件生成的客户端类
builder.Services.AddGrpcClient<GreeterClient>(options =>
{
// 单服务地址配置
options.Address = new Uri("https://localhost:5001");
});
var app = builder.Build();
// 其他中间件配置
app.Run();
配置gRPC客户端负载均衡
要实现负载均衡,需要让客户端知道多个服务实例的地址,并且配置对应的负载均衡策略。.NET的gRPC客户端支持基于ServiceDiscovery的地址解析,也可以直接配置多个静态地址,这里以静态多地址配置为例,结合轮询负载均衡策略实现。
步骤1:安装负载均衡相关依赖
使用.NET的负载均衡功能需要引入Microsoft.Extensions.Http.Resilience包,它提供了客户端负载均衡的基础能力:
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.0.0" />
步骤2:配置多服务地址和负载均衡策略
修改客户端的注册逻辑,添加多个服务地址,并配置负载均衡器:
var builder = WebApplication.CreateBuilder(args);
// 配置多个gRPC服务实例地址
var grpcServiceAddresses = new List<string>
{
"https://localhost:5001",
"https://localhost:5002",
"https://localhost:5003"
};
builder.Services.AddGrpcClient<GreeterClient>()
// 配置多个地址
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler())
.AddPolicyHandler((serviceProvider, request) =>
{
// 这里可以自定义策略,实际负载均衡通过下面的地址配置实现
return null;
})
// 配置负载均衡,使用轮询策略
.ConfigureHttpClient((serviceProvider, client) =>
{
// 客户端地址会被负载均衡器替换,这里不需要单独设置
})
// 添加负载均衡配置
.AddServiceDiscovery(options =>
{
options.ConfigureHttpClientDefaults(httpClientBuilder =>
{
httpClientBuilder.AddRoundRobinLoadBalancing();
});
});
// 手动配置服务地址列表,模拟服务发现的结果
builder.Services.Configure<ServiceDiscoveryOptions>(options =>
{
options.Services = new Dictionary<string, ServiceEndpoint[]>
{
{
"greeter-service",
grpcServiceAddresses.Select(addr => new ServiceEndpoint(new Uri(addr))).ToArray()
}
};
});
var app = builder.Build();
app.Run();
如果是实际生产环境,通常会结合服务注册中心(如Consul、Nacos)实现动态服务发现,不需要手动配置静态地址,只需要替换服务发现的配置逻辑即可。
实际调用示例
注册完成后,在控制器或者业务类中通过依赖注入获取GreeterClient实例,发起调用时客户端会自动按照配置的负载均衡策略分配请求到不同的服务实例:
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
private readonly GreeterClient _greeterClient;
// 注入gRPC客户端
public TestController(GreeterClient greeterClient)
{
_greeterClient = greeterClient;
}
[HttpGet("say-hello")]
public async Task<IActionResult> SayHello()
{
// 发起gRPC调用,客户端会自动负载均衡
var reply = await _greeterClient.SayHelloAsync(new HelloRequest { Name = "World" });
return Ok(reply.Message);
}
}
常见问题说明
- 如果服务实例有健康检查需求,可以在负载均衡配置中添加健康检查策略,自动剔除不可用的实例。
- gRPC默认使用HTTP/2协议,配置服务地址时需要确保地址支持HTTP/2,否则会出现连接失败的问题。
- 客户端工厂会复用底层HTTP连接,不需要手动管理客户端的生命周期,避免频繁创建连接带来的性能问题。
注意:负载均衡策略可以根据需求替换,除了轮询之外,还支持随机、最少请求等策略,只需要替换AddRoundRobinLoadBalancing为对应的策略方法即可。