
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/
