「譯」 .NET 6 中 gRPC 的新功能

SpringLeee發表於2021-12-09

gRPC是一個現代的、跨平臺的、高效能的 RPC 框架。gRPC for .NET 構建在 ASP.NET Core 之上,是我們推薦的在 .NET 中構建 RPC 服務的方法。

.NET 6 進一步提高了 gRPC 已經非常出色的效能,並新增了一系列新功能,使 gRPC 在現代雲原生應用程式中比以往任何時候都更好。在這篇文章中,我將描述這些新功能, 以及我們如何通過第一個支援端到端 HTTP/3 的 gRPC 實現引領行業。

gRPC 客戶端負載均衡

客戶端負載均衡功能允許 gRPC 客戶端以最佳方式在可用伺服器之間分配負載, 這樣就不需要使用專門的負載均衡代理伺服器, 這有幾個好處:

  • 效能改進, 無代理可以減少網路延遲, 因為 RPC 直接傳送到 gRPC 伺服器, 無需中轉。

  • 節省伺服器資源,負載均衡代理必須解析然後重新傳送每個 HTTP 請求, 本身也會佔用 CPU 和記憶體, 所以移除代理可以節省伺服器資源。

  • 更簡單的程式架構, gRPC 負載均衡代理需要安裝, 配置才能正常工作, 而使用客戶端負載均衡, 客戶端直接傳送到服務端, 程式的架構也很簡單。

如果要使用客戶端負載均衡, 需要在建立 channel 的時候進行配置, 另外使用負載均衡時要考慮兩個元件

  • resolver 解析器, 它可以從建立的 channel 中返回服務地址, 並且支援從外部源獲取地址, 其實這就是我們熟悉的服務發現。

  • load balancer 負載均衡器, 當呼叫 gRPC 的時候, 它會根據配置的負載均衡的策略, 返回響應的服務地址, 並建立連線。

下面的程式碼中, 給 GrpcChannel 配置了 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)
{
    // 這裡記錄錯誤並重試
}

在您的程式中, 你可能需要在很多地方寫這樣的處理程式碼, 幸運的是,.NET gRPC 客戶端現在內建了對自動重試的支援, 只需要在 channel 上統一配置即可, 並且支援幾種不同的重試策略。

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

// 發生錯誤時可以自動重試
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});

有關更多資訊,請參閱使用 gRPC 重試進行瞬態故障處理

Protobuf 效能

gRPC for .NET 使用 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();
}
Method Google.Protobuf Mean Ratio Allocated
WriteString 3.14 8.838 us 1.00 0 B
WriteString 3.18 2.919 ns 0.33 0 B

protocolbuffers/protobuf#7645 新增了一個用於建立 ByteString 例項的新 API, UnsafeByteOperations.UnsafeWrapByteString, 如果您知道底層資料不會發生改變, 那麼可以使用它建立, 這樣如果應用程式處理大位元組資料時並且您想降低垃圾收集的頻率,這將非常有用。

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 使用者反映有時下載速度會變慢, 特別時較大的檔案, 我們的調查發現,當內容大於初始的接收視窗大小時,並且客戶端和伺服器之間存在高延遲, 會導致網路阻塞和整體吞吐量降低。

這已在 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());
Method Runtime Mean Ratio
GrpcLargeDownload .NET 5.0 6.33 s 1.00
GrpcLargeDownload .NET 6.0 1.65 s 0.26

HTTP/3 支援

.NET 上的 gRPC 現在支援 HTTP/3, 其中在 .NET 6 的 ASP.NET Core 和 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 構建的應用程式以及您未來在dotnet和grpc 儲存庫中的貢獻!

作者: James Newton-King (.NET 首席軟體工程師)
原文: https://devblogs.microsoft.com/dotnet/grpc-in-dotnet-6/

「譯」 .NET 6 中 gRPC 的新功能

相關文章