gRPC 是一個現代的、跨平臺的、高效能的 RPC 框架。gRPC 是構建在 ASP.NET Core 之上,也是我們推薦的使用 .NET 構建 RPC 服務的方法。
.NET 6 進一步提高了 gRPC 已經非常出色的效能,並新增了一系列新功能,使 gRPC 在現代雲原生應用程式中比以往任何時候都更好。在這篇文章中,我將描述這些新功能以及我們如何通過第一個支援端到端 HTTP/3 的 gRPC 實現引領行業。
gPRC 客戶端負載均衡
客戶端負載平衡是一項允許 gRPC 客戶端在可用伺服器之間優化分配負載的功能。客戶端負載平衡可以消除對負載平衡代理的需要。這有幾個好處:
- 改進的效能。無代理意味著消除額外的網路躍點並減少延遲,因為 RPC 直接傳送到 gRPC 伺服器。
- 有效利用伺服器資源。負載平衡代理必須解析然後重新傳送通過它傳送的每個 HTTP 請求。刪除代理可以節省 CPU 和記憶體資源。
- 更簡單的應用程式架構。必須正確設定和配置代理伺服器。沒有代理伺服器意味著更少的活動部件!
客戶端負載均衡是在建立通道時配置的。使用負載均衡時要考慮的兩個元件:
- 解析器,解析通道的地址。解析器支援從外部源獲取地址。這也稱為服務發現。
- 負載均衡器,它建立連線並選擇 gRPC 呼叫將使用的地址。
以下程式碼示例將通道配置為使用具有迴圈負載平衡的 DNS 服務發現:
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
newGrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = newServiceConfig { LoadBalancingConfigs = { newRoundRobinConfig() } }
});
var client = newGreet.GreeterClient(channel);
var response = await client.SayHelloAsync(newHelloRequest { Name = "world" });
更多資訊,請參閱 gPRC 客戶端負載平衡。
帶有重試的瞬間故障處理
gRPC 呼叫可能會被瞬時故障中斷。瞬態故障包括:
- 網路連線暫時中斷。
- 服務暫時不可用。
- 由於伺服器負載超時。
當 gRPC 呼叫被中斷時,客戶端會丟擲一個包含錯誤詳細資訊的 RpcException。客戶端應用程式必須捕獲異常並選擇如何處理錯誤。
var client =newGreeter.GreeterClient(channel);
try
{
var response =await client.SayHelloAsync(
newHelloRequest{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 自定義重試行為。
var defaultMethodConfig =newMethodConfig
{
Names={MethodName.Default},
RetryPolicy=newRetryPolicy
{
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",newGrpcChannelOptions
{
ServiceConfig=newServiceConfig{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 指令允許並行處理多個字元,從而在序列化某些字串值時顯著提高效能。
privatestring _value =newstring(' ',10080);
privatebyte[] _outputBuffer =newbyte[10080];
[Benchmark]
publicvoidWriteString()
{
var span =newSpan<byte>(_outputBuffer);
WriteContext.Initialize(ref span,outWriteContext ctx);
ctx.WriteString(_value);
ctx.Flush();
}
protocolbuffers/protobuf#7645新增了一個用於建立 ByteString 例項的新 API。如果你知道底層資料不會改變,那麼使用 UnsafeByteOperations.UnsafeWrap 來建立一個 ByteString 而不復制底層資料。如果應用程式處理大位元組有效負載並且您想降低垃圾收集頻率,這將非常有用。
gPRC 下載速度
gRPC 使用者報告有時下載速度變慢。我們的調查發現,當客戶端和伺服器之間存在延遲時,HTTP/2 流量控制會限制下載。伺服器在客戶端可以耗盡之前填充接收緩衝區視窗,導致伺服器暫停傳送資料。gRPC 訊息以開始/停止突發方式下載。
這已在 dotnet/runtime#54755中修復。HttpClient 現在動態縮放接收緩衝區視窗。建立HTTP/2 連線後,客戶端將向伺服器傳送 ping 以測量延遲。如果存在高延遲,客戶端會自動增加接收緩衝區視窗,從而實現快速、連續的下載。
privateGrpcChannel _channel =GrpcChannel.ForAddress(...);
privateDownloadClient _client =newDownloadClient(_channel);
[Benchmark]
publicTaskGrpcLargeDownload()=>
_client.DownloadLargeMessageAsync(newEmptyMessage());
HTTP/3 支援
NET 上的 gRPC 現在支援 HTTP/3。gRPC 建立在 .NET 6 中新增到 ASP.NET Core 和 HttpClient 的 HTTP/3 支援之上。有關更多資訊,請參閱.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 客戶端和伺服器。