C#中的gRPC压缩

问题描述 投票:1回答:1

使用C#时,是否可以在gRPC中控制响应消息压缩?

This question收到了评论,但没有回答。该评论指向gRPC issue #10902,其中说:

功能就在那里,但是您需要通过设置适当的通道选项或在RPC中设置元数据条目来[启用?]。

那里连接的test class显示了一些可能性:

new WriteOptions(WriteFlags.NoCompress)

但这对我没有影响:无论此设置如何,通过WAN进行的呼叫都需要相同的时间。

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

同样,测试显示此设置更改时呼叫时间没有变化。 (CompressionRequestAlgorithmMetadataKey常量是内部的,我反编译得到字符串值)。

有一个诱人的CompressionLevel枚举in the C# github repo但我找不到任何使用它。

gRPC issue #4170(在C#中实现压缩支持)已经关闭但是说

它可以在C#中完成,但它绝对不是用户友好的

有人能够帮我提供一个关于如何做到这一点的例子吗?我不介意它不是用户友好的,我只是想能够做到!我正在通过线路发送一些大的(~8Mb)响应,我想对它们有一些控制权。虽然我可以在发送之前压缩数据,但允许客户端对所使用的方法/压缩级别进行一些控制会很好。

(我会发布一些代码,但我只是使用C# sample code中带有大型硬编码字符串的测试代码)

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:算法'gzip'已启用但决定不压缩。输入大小: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对象时,您可以传递ChannelOptions的集合。我将其设置如下:

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”:“不允许重复的元数据”,“文件”:“C:\ jenkins \ workspace \ gRPC_build_artifacts \平台\ Windows \ workspace_csharp_ext_windows_x64 \ SRC \芯\ lib中\传输\ metadata_batch.cc”, “file_line”:111, “密钥”: “GRPC编码内部请求”, “值”: “gzip的”}

这意味着我无法在每个呼叫的基础上切换和更改压缩算法或级别,但考虑到打开压缩后的性能优势,我是一个快乐的人。

c# compression grpc
1个回答
1
投票

测试#1和测试#2似乎表现得如预期。

测试#3:

  • AFAIK grpc C#目前不支持在服务器端启用压缩
  • 您尝试在服务器端使用的标头“grpc-internal-encoding-request”元数据标头应仅用于客户端
  • 您正在将标题添加到context.ResponseTrailers.Add() - 响应预告片由服务器发送,所有响应都已发送,因此无论如何都无法工作。如果支持设置压缩,则需要将标头添加到context.ResponseHeaders()。

测试#4:

  • 我相信你正在做的事情(=设置“grpc-internal-encoding-request”)是正确的,似乎你发送了一个大小为0的请求,所以grpc决定没有压缩点(有一个大小)压缩不起作用的阈值)。
© www.soinside.com 2019 - 2024. All rights reserved.