.NET 6下gRPC的新功能展示及代码示例

151 阅读5分钟

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" });

Client-side load balancing

更多信息,请参阅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平均值比率已分配
WriteString3.148.838 us1.000 B
WriteString3.182.919 ns0.330 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.06.33 s1.00
GrpcLargeDownload.NET 6.01.65 s0.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客户端和服务器的教程。

我们期待着听到用gRPC和.NET构建的应用程序,也期待着你今后在dotnetgrpc仓库中的贡献!