文章目錄
C# (.NET Core 2) 啟用 gRPC 壓縮
目前專案在大資料量傳遞時會透過 gRPC stream,不過因為是非對稱式資料內容,採用 chunk byte 來傳輸,以避免單次 gRPC 的 message size 限制問題,但以當前的資料結構,資料量還是過大,導致處理時間無法有效縮短,同事提到想要使用壓縮來縮小資料量以減少傳輸的成本以及時間,不過測試下來似乎沒有減少傳輸時間,懷疑是壓縮沒有順利啟用
為了查明到底是 gRPC 壓縮沒有成功啟用,還是有其他原因造成傳輸時間沒有減少,就得先開啟 gRPC 的 tracer,詳細做法可以參考之前筆記 C# (.NET Core 2) Log 與 Trace gRPC
而今天就是來紀錄一下,gRPC 啟用壓縮的做法
基本環境說明
- macOS Majave 10.14.16
- .NET Core SDK 2.2.301
- JetBrains Rider 2019.2.2
ASP.NET Core MVC 2.2 預設專案範本
程式碼部份可參考 C# 搭配 gRPC 中使用 stream RPC
NuGet Package
- Google.Protobuf 3.8.0
- Grpc 2.25.0
- Grpc.Tools 2.25.0
- BenchmarkDotNet 0.12.0
未壓縮下 message 大於 4194304 (4mb)
使用重複資料 * 5000 當做單次 message 內容
Grpc.Core.RpcException: Status(StatusCode=ResourceExhausted, Detail="Received message larger than max (4860007 vs. 4194304)") at Grpc.Core.Internal.ClientResponseStream`2.MoveNext(CancellationToken token) in T:\src\github\grpc\src\csharp\Grpc.Core\Internal\ClientResponseStream.cs:line 60 at Grpc.Core.Utils.AsyncStreamExtensions.ToListAsync[T](IAsyncStreamReader`1 streamReader) in T:\src\github\grpc\src\csharp\Grpc.Core\Utils\AsyncStreamExtensions.cs:line 49 at GRpc.Client.Controllers.CandidateController.GetAllCandidate() in /Users/yowko.tsai/DotnetGrpcStream/GRpc.Client/Controllers/CandidateController.cs:line 162
啟用 gRPC 壓縮
做法一: 使用
gzip
壓縮設定方式
在單一 gRPC service 中設定
public override async Task DownloadAllCv(Empty request, IServerStreamWriter<Candidate> responseStream,ServerCallContext context) { //設定使用 gzip 開始 var compressionMetadata = new Metadata { {new Metadata.Entry("grpc-internal-encoding-request", "gzip")} }; await context.WriteResponseHeadersAsync(compressionMetadata); //設定使用 gzip 結束 foreach (var candidate in createRequests) { await responseStream.WriteAsync(candidate); } }
實際效果
可以看到 grpc 的 log 已經顯示出
Compressed[gzip]
,以本次模擬的重複資料內容而言節省了99.76%
的傳輸空間I1117 18:25:10.405264 123145570623488 /tmpfs/src/github/grpc/workspace_csharp_ext_macos_x64/src/core/ext/filters/http/message_compress/message_compress_filter.cc:265: Compressed[gzip] 4050007 bytes vs. 9888 bytes (99.76% savings)
做法二: 使用
deflate
壓縮設定方式
設定 gRPC Channel 的壓縮比 (這會套用至所有 gRPC service)
var serverInstance = new Grpc.Core.Server( // 啟用 deflate 壓縮開啟 new[] {new ChannelOption("grpc.default_compression_level", (int) CompressionLevel.High)} // 啟用 deflate 壓縮結束 ) { Ports = { new ServerPort(host, port, ServerCredentials.Insecure) } };
實際效果
可以看到 grpc 的 log 已經顯示出
Compressed[deflate]
,以本次模擬的重複資料內容而言節省了99.76%
的傳輸空間I1117 18:44:54.079455 123145484414976 /tmpfs/src/github/grpc/workspace_csharp_ext_macos_x64/src/core/ext/filters/http/message_compress/message_compress_filter.cc:265: Compressed[deflate] 4050007 bytes vs. 9876 bytes (99.76% savings)
效能測試比較
第一次
Method Mean Error StdDev Gzip 323.6 ms 6.36 ms 9.72 ms Deflate 306.6 ms 6.04 ms 11.92 ms 第二次
Method Mean Error StdDev Gzip 376.1 ms 14.12 ms 41.40 ms Deflate 347.4 ms 6.94 ms 19.81 ms 第三次
Method Mean Error StdDev Gzip 311.7 ms 6.16 ms 12.29 ms Deflate 332.4 ms 8.95 ms 25.53 ms
心得
以測試結果來看,互有勝負,沒有絕對哪一個方法比較好,若以使用方式來看 gzip
需要逐一在每個 service 上加入設定, deflate
則是一次全套用;看似 deflate
比較方便,但 gRPC compression in C# 也提到啟用壓縮不一定會讓回應時間變快,在壓縮的處理也有一定的成本,到底該不該壓縮或是壓縮比例都需要經過測試的,為了符合不同情境,也可以在使用 deflate
針對特定 service 停用壓縮 - 透過在 service 中加上 responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
花了不少時間測試,終於找到 gRPC 的 log 與壓縮的正確設定方式,相對應的做法在 .NET Core 3 會簡單不少,如果沒有特別的考量,建議還是升級 .NET Core 3 吧
參考資訊
文章作者 Yowko Tsai
上次更新 2020-12-11
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。