簡體   English   中英

C# 中的 gRPC 壓縮

[英]gRPC compression in C#

使用 C# 時是否可以控制 gRPC 中的響應消息壓縮?

這個問題收到了評論,但沒有答案。 該評論指向gRPC 問題 #10902 ,其中說:

功能在那里,但您需要通過設置適當的通道選項或在您的 RPC 中設置元數據條目來[啟用?]。

鏈接到那里的測試類顯示了幾種可能性:

new WriteOptions(WriteFlags.NoCompress)

但這對我沒有影響:無論此設置如何,WAN 上的呼叫都需要相同的時間。

var compressionMetadata = new Metadata
{
    { new Metadata.Entry(Metadata.CompressionRequestAlgorithmMetadataKey, "gzip") }
};

同樣,測試顯示更改此設置時呼叫時間沒有變化。 CompressionRequestAlgorithmMetadataKey常量是內部的,我反編譯得到字符串值)。

C# github repo 中有一個誘人的CompressionLevel枚舉但我找不到它的任何用法。

gRPC 問題 #4170 (在 C# 中實現壓縮支持)已關閉,但表示

它可以在 C# 中完成,但它絕對不是用戶友好的

有沒有人能幫我舉個例子來說明如何做到這一點? 如果它不是用戶友好的,我不介意,我只是希望能夠做到! 我正在通過網絡發送一些大的(~8Mb)響應,我想對它們進行一些控制。 雖然我可以在發送之前壓縮數據,但允許客戶端對使用的方法/壓縮級別進行一些控制會很好。

(我會發布一些代碼,但我只是使用帶有大型硬編碼字符串的C# 示例代碼中的測試代碼)

tl;dr 我不知道如何使用 C# gRPC 控制壓縮設置。 所有看起來可能的設置都不會影響通過線路發送響應所花費的時間。




編輯
在 Jon Skeet 關於查看線路交通的評論之后,我運行了一個新測試,其中響應是字符串The quick brown fox jumps over the lazy dog重復了 5000 次。 我通過以下方式打開了 gRPC 跟蹤

        Environment.SetEnvironmentVariable("GRPC_TRACE", "compression");
        Environment.SetEnvironmentVariable("GRPC_VERBOSITY", "DEBUG");
        GrpcEnvironment.SetLogger(new ConsoleLogger());

...在客戶端和服務器上。 我使用 Wireshark 捕獲網絡流量,解碼我的 gRPC 服務器端口上的 HTTP2 流量。

測試 #1:沒有特定的代碼來控制壓縮

  • 可以看到流量沒有被壓縮。

測試 #2:服務器關閉壓縮

  • 設置context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); 在返回響應之前在服務器端。
  • 可以看到流量沒有被壓縮。

測試 #3:服務器啟用壓縮

  • 設置context.ResponseTrailers.Add(new Metadata.Entry("grpc-internal-encoding-request", "gzip")); 在返回響應之前在服務器端。
  • 可以看到流量沒有被壓縮。

測試 #4:客戶端啟用壓縮

  • 調用服務器時設置元數據頭: var headers = new Metadata {{new Metadata.Entry("grpc-internal-encoding-request", "gzip")}};
  • 客戶端 gRPC 跟蹤記錄以下內容

D0228 16:53:21.566026 0 C:\\jenkins\\workspace\\gRPC_build_artifacts\\platform\\windows\\workspace_csharp_ext_windows_x64\\src\\core\\ext\\filters\\http\\message_compress\\message_compress_filter.cc:262:算法未啟用但已決定壓縮. 輸入大小:0

  • 我認為這是試圖壓縮請求而不是響應
  • 可以看到流量沒有被壓縮。




編輯 #2
Jan Tattermusch 明白這一點。 在我的服務器端方法中添加此代碼

        var headers = new Metadata{new Metadata.Entry("grpc-internal-encoding-request", "gzip")};            
        await context.WriteResponseHeadersAsync(headers);

……成功了! Wireshark 顯示響應被壓縮並顯示 gRPC 日志

壓縮 [gzip] 225002 字節與 742 字節(節省 99.67%)

傑出的! 可悲的是,它導致了我的下一個問題:壓縮的開銷導致此方法調用比未壓縮的數據
更多的谷歌搜索讓我找到了更好的選擇:創建Server對象時,您可以傳遞ChannelOption的集合。 我設置如下:

var options = new[] {new ChannelOption("grpc.default_compression_level", (int) CompressionLevel.Low)};

這將壓縮設置為更好的級別(對於我在此處發送的數據),並且具有啟用 gzip 壓縮作為所有調用的默認值的副作用。 現在每個方法都必須明確關閉壓縮(通過我的測試 #2 中的WriteOptions )。

有趣的是,嘗試通過grpc-internal-encoding-request覆蓋默認壓縮現在非常慢,我猜是由於引發了異常:

src\\core\\lib\\surface\\call.cc:1018: prepare_application_metadata: {"created":"@1520008670.738000000","description":"不允許重復的元數據","file":"C:\\jenkins\\workspace\\gRPC_build_artifacts\\平台\\windows\\workspace_csharp_ext_windows_x64\\src\\core\\lib\\transport\\metadata_batch.cc","file_line":111,"key":"grpc-internal-encoding-request","value":"gzip"}

這意味着我不能在每次調用的基礎上修改和更改壓縮算法或級別,但考慮到打開壓縮的性能優勢,我是一個快樂的人。

測試 #1 和測試 #2 似乎按預期運行。

測試#3:

  • AFAIK grpc C# 目前不支持在服務器端啟用壓縮
  • 您嘗試在服務器端使用的標頭“grpc-internal-encoding-request”元數據標頭只能在客戶端使用
  • 您正在將標頭添加到 context.ResponseTrailers.Add() - 服務器在傳輸所有響應后發送響應預告片,因此無論如何這都沒有機會工作。 如果支持設置壓縮,則需要將標頭添加到 context.ResponseHeaders() 中。

測試#4:

  • 我相信你在做什么(=設置“grpc-internal-encoding-request”)是正確的,看起來你正在發送一個大小為0的請求,所以grpc決定沒有壓縮的意義(有一個大小不進行壓縮的閾值)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM