C#中的gRPC压缩

时间:2018-02-28 14:21:33

标签: c# compression grpc

使用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:Algorithm' gzip&# 39;启用但决定不压缩。输入大小: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":& #34;不允许重复的元数据","文件":" C:\ jenkins \ workspace \ gRPC_build_artifacts \ platform \ windows \ workspace_csharp_ext_windows_x64 \ src \ core \ lib \ transport \ metadata_batch.cc&# 34;," file_line":111,"键":" GRPC内部编码请求""值":&#34 ; gzip的"}

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

1 个答案:

答案 0 :(得分:1)

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

测试#3:

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

测试#4:

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