gRPC是一个现代的、跨平台的、高性能的RPC框架。gRPC for .NET是建立在ASP.NET Core之上的,是我们推荐的使用.NET构建RPC服务的方式。
.NET 6进一步提高了gRPC已经很好的性能,并增加了一系列新的功能,使gRPC在现代云原生应用程序中比以前更好。在这篇文章中,我将介绍这些新功能,以及我们如何通过第一个支持端到端HTTP/3的gRPC实现来引领行业。
gRPC客户端负载均衡
客户端负载平衡是一个允许gRPC客户端在可用的服务器上最佳地分配负载的功能。客户端负载平衡可以消除对负载平衡的代理的需要。这有几个好处:
- 提高性能。没有代理意味着消除了一个额外的网络跳跃,并减少了延迟,因为RPC被直接发送到gRPC服务器上。
- 有效地使用服务器资源。负载平衡代理必须解析并重新发送每个通过它发送的HTTP请求。移除代理可以节省CPU和内存资源。
- 更简单的应用架构。代理服务器必须被正确设置和配置。没有代理服务器意味着更少的移动部件!
客户端负载平衡是在创建一个通道时配置的。使用负载平衡时要考虑的两个组件:
- 解析器,它为通道解析地址。解析器支持从外部来源获得地址。这也被称为服务发现。
- 负载均衡器,它创建连接并选择gRPC调用将使用的地址。
下面的代码示例将一个通道配置为使用DNS服务发现与轮流负载平衡:
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" });

更多信息,请参阅gRPC客户端负载平衡。
带有重试的瞬时故障处理
gRPC调用可能会被瞬时故障打断。瞬时故障包括:
- 瞬间失去网络连接。
- 服务的暂时不可用。
- 由于服务器负载导致的超时。
当一个gRPC调用被中断时,客户端会抛出一个RpcException ,其中包含错误的细节。客户端应用程序必须捕捉到这个异常,并选择如何处理这个错误:
var client = new Greeter.GreeterClient(channel);
try
{
var response = await client.SayHelloAsync(
new HelloRequest { Name = ".NET" });
Console.WriteLine("From server: " + response.Message);
}
catch (RpcException ex)
{
// Write logic to inspect the error and retry
// if the error is from a transient fault.
}
在整个应用程序中重复重试逻辑是冗长和容易出错的。幸运的是,.NET gRPC客户端现在内置了对自动重试的支持。重试是在一个通道上集中配置的,并且有许多选项可以使用RetryPolicy.NET来定制重试行为:
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 }
}
};
// Clients created with this channel will automatically retry failed calls.
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});
欲了解更多信息,请参见使用gRPC重试的瞬时故障处理。
Protobuf性能
.NET的gRPC使用Google.Protobuf包作为消息的默认串行器。Protobuf是一种高效的二进制序列化格式。Google.Protobuf是为性能而设计的,使用代码生成而不是反射来序列化.NET对象。在.NET 5中,我们与Protobuf团队合作,在序列化器中增加了对现代内存API的支持,如Span<T>,ReadOnlySequence<T>, 和IBufferWriter<T> 。在.NET 6中的改进优化了一个已经很快速的序列化器。
protocolbuffers/protobuf#8147增加了矢量字符串的序列化。SIMD指令允许多个字符被并行处理,在序列化某些字符串值时极大地提高了性能。
private string _value = new string(' ', 10080);
private byte[] _outputBuffer = new byte[10080];
[Benchmark]
public void WriteString()
{
var span = new Span<byte>(_outputBuffer);
WriteContext.Initialize(ref span, out WriteContext ctx);
ctx.WriteString(_value);
ctx.Flush();
}
| 方法 | Google.Protobuf | 平均值 | 比率 | 已分配 |
|---|---|---|---|---|
| WriteString | 3.14 | 8.838 us | 1.00 | 0 B |
| WriteString | 3.18 | 2.919 ns | 0.33 | 0 B |
protocolbuffers/protobuf#7645添加了一个新的API用于创建ByteString 实例。如果你知道底层数据不会改变,那么使用UnsafeByteOperations.UnsafeWrap 来创建一个ByteString ,而不用复制底层数据。如果一个应用程序使用大字节的有效载荷,并且你想减少垃圾收集的频率,这就非常有用:
var data = await File.ReadAllBytesAsync(@"c:large_file.json");
// Safe but slow.
var copied = ByteString.CopyFrom(data);
// Unsafe but fast. Useful if you know data won't change.
var wrapped = UnsafeByteOperations.UnsafeWrap(data);
gRPC的下载速度
gRPC用户报告说,有时会得到缓慢的下载速度。我们的调查发现,当客户端和服务器之间存在延迟时,HTTP/2流量控制会限制下载。服务器在客户端耗尽之前填满了接收缓冲区窗口,导致服务器暂停发送数据。
这在dotnet/runtime#54755中得到了修复。HttpClient ,现在可以动态地调整接收缓冲区窗口的大小。当HTTP/2连接建立时,客户端将向服务器发送一个ping以测量延迟。如果存在高延迟,客户端会自动增加接收缓冲区窗口,从而实现快速、连续的下载。
private GrpcChannel _channel = GrpcChannel.ForAddress(...);
private DownloadClient _client = new DownloadClient(_channel);
[Benchmark]
public Task GrpcLargeDownload() =>
_client.DownloadLargeMessageAsync(new EmptyMessage());
| 方法 | 运行时间 | 平均值 | 比率 |
|---|---|---|---|
| GrpcLargeDownload | .NET 5.0 | 6.33 s | 1.00 |
| GrpcLargeDownload | .NET 6.0 | 1.65 s | 0.26 |
支持HTTP/3
.NET上的gRPC现在支持HTTP/3。gRPC建立在ASP.NET Core和.NET 6中添加的HTTP/3支持之上,HttpClient 。欲了解更多信息,请参阅.NET 6中的HTTP/3支持。
.NET是第一个支持端到端HTTP/3的gRPC实现,我们已经提交了一个gRFC,以便其他平台在未来支持HTTP/3。带HTTP/3的gRPC是开发者社区强烈要求的功能,看到.NET在这个领域的领先地位,我们感到非常兴奋。
总结
性能是.NET和gRPC的特点,而.NET 6比以前更快。新的以性能为导向的功能,如客户端负载平衡和HTTP/3,意味着更低的延迟,更高的吞吐量,以及更少的服务器。这是一个节省资金、减少电力使用和建立更环保的云原生应用程序的机会。
要尝试新的功能并开始使用.NET的gRPC,最好的开始是在ASP.NET Core中创建gRPC客户端和服务器的教程。