文档
-
了解如何在 .NET 中利用重试进行可复原的容错 gRPC 调用。
-
了解如何使用客户端工厂创建 gRPC 客户端。
-
了解如何在 .NET 中创建具有截止时间和取消功能的可靠的 gRPC 服务。
备注
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文的 .NET 9 版本。
客户端负载均衡功能允许 gRPC 客户端以最佳方式在可用服务器之间分配负载。 本文介绍了如何配置客户端负载均衡,以在 .NET 中创建可缩放的高性能 gRPC 应用。
使用客户端负载均衡需要具备以下组件:
Grpc.Net.Client
版本 2.45.0 或更高版本。客户端负载均衡是在创建通道时配置的。 使用负载均衡时需要考虑两个组件:
解析程序和负载均衡器的内置实现包含在 Grpc.Net.Client
中。 也可以通过编写自定义解析程序和负载均衡器来扩展负载均衡。
地址、连接和其他负载均衡状态存储在 GrpcChannel
实例中。 在进行 gRPC 调用时,必须重用通道,以使负载均衡正常工作。
备注
某些负载均衡配置使用依赖项注入 (DI)。 不使用 DI 的应用可以创建 ServiceCollection 实例。
如果应用已设置 DI(如 ASP.NET Core 网站),则应向现有 DI 实例注册类型。 GrpcChannelOptions.ServiceProvider
是通过从 DI 获取 IServiceProvider 来配置的。
解析程序是使用创建通道时所用的地址配置的。 地址的 URI 方案指定解析程序。
Scheme | 类型 | 说明 |
---|---|---|
dns |
DnsResolverFactory |
通过查询 DNS 地址记录的主机名来解析地址。 |
static |
StaticResolverFactory |
解析应用已指定的地址。 如果应用已经知道它调用的地址,则建议使用。 |
通道不会直接调用与解析程序匹配的 URI。 而是创建一个匹配的解析程序,用它来解析地址。
例如,使用 GrpcChannel.ForAddress("dns:///my-example-host", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure })
:
dns
方案映射到 DnsResolverFactory
。 为通道创建 DNS 解析程序的一个新实例。my-example-host
进行 DNS 查询,并获得两个结果:127.0.0.100
和 127.0.0.101
。127.0.0.100:80
和 127.0.0.101:80
创建连接并进行 gRPC 调用。DnsResolverFactory
创建一个解析程序,旨在从外部源获取地址。 DNS 解析通常用于对具有 Kubernetes 无外设服务的 Pod 实例进行负载均衡。
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
前面的代码:
dns:///my-example-host
配置已创建的通道。
dns
方案映射到 DnsResolverFactory
。my-example-host
是要解析的主机名。dns:///my-example-host:8080
将 gRPC 调用配置为发送到端口 8080。SayHello
:my-example-host
获取地址。在使用负载均衡时,性能非常重要。 通过缓存地址,从 gRPC 调用中消除了解析地址的延迟。 进行第一次 gRPC 调用时,将调用解析程序,后续调用将使用缓存。
如果连接中断,则会自动刷新地址。 如果是在运行时更改地址,那么刷新非常重要。 例如,在 Kubernetes 中,重启的 Pod 会触发 DNS 解析程序刷新并获取 Pod 的新地址。
默认情况下,如果连接中断,则会刷新 DNS 解析程序。 DNS 解析程序还可以根据需要定期刷新。 这对于快速检测新的 pod 实例很有用。
services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
上面的代码创建具有刷新间隔的 DnsResolverFactory
,并将其注册到依赖关系注入。 有关使用自定义配置解析程序的详细信息,请参阅配置自定义解析程序负载均衡器。
静态解析程序由 StaticResolverFactory
提供。 此解析程序:
var factory = new StaticResolverFactory(addr => new[]
{
new BalancerAddress("localhost", 80),
new BalancerAddress("localhost", 81)
});
var services = new ServiceCollection();
services.AddSingleton<ResolverFactory>(factory);
var channel = GrpcChannel.ForAddress(
"static:///my-example-host",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceProvider = services.BuildServiceProvider()
});
var client = new Greet.GreeterClient(channel);
上述代码:
StaticResolverFactory
。 此工厂知道两个地址:localhost:80
和 localhost:81
。static:///my-example-host
。 static
方案映射到静态解析程序。GrpcChannelOptions.ServiceProvider
。本示例为 DI 创建了一个新的 ServiceCollection。 假设一个应用已设置 DI,比如一个 ASP.NET Core 网站。 在这种情况下,类型应被注册到现有的 DI 实例。 GrpcChannelOptions.ServiceProvider
是通过从 DI 获取 IServiceProvider 来配置的。
负载均衡器是使用 ServiceConfig.LoadBalancingConfigs
集合在 service config
中指定的。 两个负载均衡器都是内置的,映射到负载均衡器配置名称:
名称 | Type | 说明 |
---|---|---|
pick_first |
PickFirstLoadBalancerFactory |
尝试连接到地址,直到成功建立连接。 gRPC 调用都是针对第一次成功连接进行的。 |
round_robin |
RoundRobinLoadBalancerFactory |
尝试连接到所有地址。 gRPC 调用是使用轮循机制逻辑分布在所有成功的连接上的。 |
service config
是“service configuration”的缩写形式,用 ServiceConfig
类型表示。 有几种方法可以让通道获取配置了负载均衡器的 service config
:
GrpcChannelOptions.ServiceConfig
创建通道时,应用可以指定 service config
。service config
。 此功能允许外部源指定其调用方应如何执行负载均衡。 解析程序是否支持解析 service config
取决于解析程序实现。 使用 GrpcChannelOptions.DisableResolverServiceConfig
禁用此功能。service config
,或者 service config
没有配置负载均衡器,则通道默认为 PickFirstLoadBalancerFactory
。var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new RoundRobinConfig() } }
});
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
前面的代码:
service config
中指定了 RoundRobinLoadBalancerFactory
。SayHello
:DnsResolverFactory
创建一个解析程序,用于为主机名 my-example-host
获取地址。通道必须知道是否使用传输安全性发送了 gRPC 调用。 http
和 https
不再是地址的一部分,方案现在指定一个解析程序,使得在使用负载均衡时必须对通道选项配置 Credentials
。
ChannelCredentials.SecureSsl
- gRPC 调用使用ChannelCredentials.SecureSsl
进行保护。 等同于 https
地址。ChannelCredentials.Insecure
- gRPC 调用不使用传输安全性。 等同于 http
地址。var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
可将 gRPC 客户端工厂配置为使用负载均衡:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("dns:///my-example-host");
})
.ConfigureChannel(o => o.Credentials = ChannelCredentials.Insecure);
builder.Services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
var app = builder.Build();
前面的代码:
客户端负载均衡具有可扩展性,可以:
Resolver
以创建自定义解析程序,并解析来自新数据源的地址。LoadBalancer
以使用新的负载均衡行为创建自定义负载均衡器。重要
用于扩展客户端负载均衡的 API 是实验性的。 它们可能会更改,恕不另行通知。
解析程序:
Resolver
并由 ResolverFactory
创建。 通过实现这些类型创建自定义解析程序。public class FileResolver : PollingResolver
{
private readonly Uri _address;
private readonly int _port;
public FileResolver(Uri address, int defaultPort, ILoggerFactory loggerFactory)
: base(loggerFactory)
{
_address = address;
_port = defaultPort;
}
public override async Task ResolveAsync(CancellationToken cancellationToken)
{
// Load JSON from a file on disk and deserialize into endpoints.
var jsonString = await File.ReadAllTextAsync(_address.LocalPath);
var results = JsonSerializer.Deserialize<string[]>(jsonString);
var addresses = results.Select(r => new BalancerAddress(r, _port)).ToArray();
// Pass the results back to the channel.
Listener(ResolverResult.ForResult(addresses));
}
}
public class FileResolverFactory : ResolverFactory
{
// Create a FileResolver when the URI has a 'file' scheme.
public override string Name => "file";
public override Resolver Create(ResolverOptions options)
{
return new FileResolver(options.Address, options.DefaultPort, options.LoggerFactory);
}
}
在上述代码中:
FileResolverFactory
可实现 ResolverFactory
。 它映射到 file
方案并创建 FileResolver
实例。FileResolver
可实现 PollingResolver
。 PollingResolver
是一个抽象基类,通过重写 ResolveAsync
可以轻松地通过异步逻辑实现解析程序。ResolveAsync
中:file:///c:/addresses.json
重命名为 c:\addresses.json
。负载均衡器:
LoadBalancer
并由 LoadBalancerFactory
创建。 通过实现这些类型创建自定义负载均衡器和工厂。Subchannel
实例。SubchannelPicker
。 通道在内部使用选取器,以在进行 gRPC 调用时选取地址。SubchannelsLoadBalancer
:
LoadBalancer
的抽象基类。Subchannel
实例的过程。public class RandomBalancer : SubchannelsLoadBalancer
{
public RandomBalancer(IChannelControlHelper controller, ILoggerFactory loggerFactory)
: base(controller, loggerFactory)
{
}
protected override SubchannelPicker CreatePicker(List<Subchannel> readySubchannels)
{
return new RandomPicker(readySubchannels);
}
private class RandomPicker : SubchannelPicker
{
private readonly List<Subchannel> _subchannels;
public RandomPicker(List<Subchannel> subchannels)
{
_subchannels = subchannels;
}
public override PickResult Pick(PickContext context)
{
// Pick a random subchannel.
return PickResult.ForSubchannel(_subchannels[Random.Shared.Next(0, _subchannels.Count)]);
}
}
}
public class RandomBalancerFactory : LoadBalancerFactory
{
// Create a RandomBalancer when the name is 'random'.
public override string Name => "random";
public override LoadBalancer Create(LoadBalancerOptions options)
{
return new RandomBalancer(options.Controller, options.LoggerFactory);
}
}
在上述代码中:
RandomBalancerFactory
可实现 LoadBalancerFactory
。 它映射到 random
策略名称并创建 RandomBalancer
实例。RandomBalancer
可实现 SubchannelsLoadBalancer
。 它创建一个随机选择子通道的 RandomPicker
。使用自定义解析程序和负载均衡器时,需要在依赖项注入 (DI) 中注册。 有以下几种方式:
GrpcChannelOptions.ServiceProvider
将它传递到通道。var services = new ServiceCollection();
services.AddSingleton<ResolverFactory, FileResolverFactory>();
services.AddSingleton<LoadBalancerFactory, RandomLoadBalancerFactory>();
var channel = GrpcChannel.ForAddress(
"file:///c:/data/addresses.json",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new LoadBalancingConfig("random") } },
ServiceProvider = services.BuildServiceProvider()
});
var client = new Greet.GreeterClient(channel);
前面的代码:
ServiceCollection
并注册新的解析程序和负载均衡器实现。ServiceCollection
内置于 IServiceProvider
中,并设置为 GrpcChannelOptions.ServiceProvider
。file:///c:/data/addresses.json
。 file
方案映射到 FileResolverFactory
。service config
负载均衡器名称为 random
。 映射到 RandomLoadBalancerFactory
。HTTP/2 在单个 TCP 连接上多路复用多个调用。 如果将 gRPC 和 HTTP/2 与网络负载均衡器 (NLB) 结合使用,连接将被转发到服务器,并且所有 gRPC 调用都将被发送到该服务器。 NLB 上的其他服务器实例处于空闲状态。
网络负载均衡器是负载均衡的一种常见解决方案,因为它们速度快,并且是轻量级的。 例如,Kubernetes 默认使用网络负载均衡器来均衡 Pod 实例之间的连接。 然而,与 gRPC 和 HTTP/2 一起使用时,网络负载均衡器不能有效地分配负载。
可以使用应用程序负载均衡器代理或客户端负载均衡方式有效地对 gRPC 和 HTTP/2 进行负载均衡。 这两种方式都允许在所有可用服务器中分布各个 gRPC 调用。 在代理和客户端负载均衡之间做出决策是一种体系结构选择。 每个选项都有优缺点。
代理:gRPC 调用被发送到代理,代理做出负载均衡决策,然后 gRPC 调用被发送到最终终结点。 代理负责了解终结点。 使用代理添加:
客户端负载均衡:启动 gRPC 调用时,gRPC 客户端做出负载均衡决策。 gRPC 调用被直接发送到最终终结点。 使用客户端负载均衡时:
文档
了解如何在 .NET 中利用重试进行可复原的容错 gRPC 调用。
了解如何使用客户端工厂创建 gRPC 客户端。
了解如何在 .NET 中创建具有截止时间和取消功能的可靠的 gRPC 服务。
培训
模块
在 Azure 中对非 HTTP(S) 流量进行负载均衡 - Training
了解 Azure 中的不同负载均衡器选项,以及如何选择和实施适用于非 HTTP(S) 流量的 Azure 解决方案。