
gRPC C 消息压缩实战指南从 Channel 级默认压缩到单次 RPC 的压缩覆盖【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc消息压缩是 gRPC 在网络上节省带宽的核心手段之一。本文以 gRPC C 仓库中的 compression 示例为主线带你完整掌握如何在客户端与服务端分别配置默认压缩算法、如何通过 ClientContext / ServerContext 针对单次 RPC 覆盖压缩配置、如何理解压缩算法枚举与 channel 参数背后的底层映射以及压缩协商在 HTTP/2 线缆协议层面的错误语义。读完本文你将能够在自己的 gRPC C 服务中自如落地消息压缩配置并读懂与压缩相关的错误码与 header 行为。前置知识从 Hello World 起步本文构建在 Hello World 示例 的基础上默认你已经跑通过examples/cpp/helloworld理解 gRPC 的基本工作方式proto 定义、代码生成、Stub 与 Server 的搭建。压缩示例复用了同一个 helloworld.proto其中定义了Greeter服务与SayHello一元 RPCsyntax proto3; package helloworld; service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} // ... 其他 streaming 方法 } message HelloRequest { string name 1; } message HelloReply { string message 1; }在仓库中该示例位于examples/cpp/compression目录包含greeter_client.cc、greeter_server.cc两个源文件以及 Makefile、CMakeLists.txt、BUILDBazel三套构建入口。查看完整示例代码请参考 greeter_client.cc 与 greeter_server.cc。获取示例源码与进入目录本文属于 gRPCexamples目录下的 C 示例。若你尚未拉取仓库可克隆 gRPC 官方仓库并切换到最新的稳定发布 tagRELEASE_TAG_HERE请替换为真实 tag如v1.6x.x$ git clone -b RELEASE_TAG_HERE https://github.com/grpc/grpc进入压缩示例所在目录$ cd examples/cpp/compression/需要说明的是本文依赖的 helloworld.proto 位于examples/protos目录示例代码通过相对路径../../protos/引用它。生成 gRPC 代码方式一Makefile 目标示例自带的 Makefile 通过模式规则把.proto生成.pb.cc与.grpc.pb.cc。直接运行$ make helloworld.grpc.pb.cc helloworld.pb.cc该命令内部等价于先后调用 protoc 与 grpc_cpp_plugin 完成两次生成——一次产出 gRPC 服务桩代码--grpc_out一次产出 protobuf 消息代码--cpp_out$ protoc -I ../../protos/ --grpc_out. --pluginprotoc-gen-grpcgrpc_cpp_plugin ../../protos/helloworld.proto $ protoc -I ../../protos/ --cpp_out. ../../protos/helloworld.proto两条命令均以examples/protos为-I头文件搜索路径最终在当前目录生成helloworld.pb.h/helloworld.pb.ccHelloRequest、HelloReply等消息类helloworld.grpc.pb.h/helloworld.grpc.pb.ccGreeter::Stub客户端与Greeter::Service服务端基类。方式二CMake 与 Bazel基于 CMake 时CMakeLists.txt 通过add_custom_command直接调用_PROTOBUF_PROTOC与grpc_cpp_plugin--pluginprotoc-gen-grpc${_GRPC_CPP_PLUGIN_EXECUTABLE}并把生成源文件编入hw_grpc_proto静态库随后构建greeter_client与greeter_server两个可执行目标。基于 Bazel 时BUILD 中定义compression_client与compression_server两个cc_binary依赖//:grpc与//examples/protos:helloworld_cc_grpc并通过defines [BAZEL_BUILD]让源码走#ifdef BAZEL_BUILD分支、按examples/protos/helloworld.grpc.pb.h的路径去 include 生成头文件。编写客户端Channel 级默认压缩 单次 RPC 覆盖压缩示例的客户端与 Hello World 客户端结构一致区别在于引入了两层压缩配置。完整代码见 greeter_client.cc。第 1 层通过 ChannelArguments 设置 Channel 默认压缩算法在创建 Channel 之前先构造grpc::ChannelArguments并调用SetCompressionAlgorithm将其作为默认压缩算法传入grpc::CreateCustomChannelChannelArguments args; // Set the default compression algorithm for the channel. args.SetCompressionAlgorithm(GRPC_COMPRESS_GZIP); GreeterClient greeter(grpc::CreateCustomChannel( localhost:50051, grpc::InsecureChannelCredentials(), args));这段配置的语义是凡是没有显式指定压缩算法的 RPC都按 GZIP 进行压缩。可以从源码印证其底层机制——channel_arguments.cc 中该方法仅是把枚举值映射为 channel 整数参数grpc.default_compression_algorithmvoid ChannelArguments::SetCompressionAlgorithm( grpc_compression_algorithm algorithm) { SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm); }该参数名常量定义在 compression_types.h属于 C 层 channel 参数体系GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM。除了默认算法头文件中还声明了两个相关的 channel 参数grpc.default_compression_levelGRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL默认压缩级别未设置时为GRPC_COMPRESS_LEVEL_NONEgrpc.compression_enabled_algorithms_bitsetGRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET以位图声明 Channel 支持的算法集合未设置位即禁用对应算法默认全部支持且不允许禁用GRPC_COMPRESS_NONE。第 2 层通过 ClientContext 覆盖单次调用的压缩算法每个 RPC 的压缩配置可以在ClientContext上再次覆盖。示例在SayHello中把本次调用的算法从 Channel 默认的 GZIP 改为 DEFLATEClientContext context; // Overwrite the calls compression algorithm to DEFLATE. context.set_compression_algorithm(GRPC_COMPRESS_DEFLATE); Status status stub_-SayHello(context, request, reply);ClientContext::set_compression_algorithm的公开接口声明于 client_context.h。这样在同一 Channel 之上不同 RPC 可以各用各的压缩算法优先级为「调用级配置 Channel 默认配置」。一个便于观察效果的细节示例客户端发送的用户名是world world world world重复四次这是为了让待压缩的载荷具备一定冗余度便于观察压缩效果。编写服务端Server 级默认压缩 按调用覆盖服务端逻辑同样建立在 Hello World 服务实现之上完整代码见 greeter_server.cc。通过 ServerBuilder 设置服务端默认压缩算法在ServerBuilder上调用SetDefaultCompressionAlgorithm即设定整个 Server 的默认压缩算法ServerBuilder builder; // Set the default compression algorithm for the server. builder.SetDefaultCompressionAlgorithm(GRPC_COMPRESS_GZIP); builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(service); std::unique_ptrServer server(builder.BuildAndStart());该方法声明于 server_builder.h。同一个头文件中还有一个值得了解的配套接口SetCompressionAlgorithmSupportStatusserver_builder.h用于控制服务端是否开启对某算法的支持能力判断展示了「默认算法」与「算法可用性」是两套相互独立的配置维度。通过 ServerContext 覆盖单次调用的压缩算法服务端在方法实现内通过ServerContext覆盖本次响应的压缩算法class GreeterServiceImpl final : public Greeter::Service { Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override { // Overwrite the calls compression algorithm to DEFLATE. context-set_compression_algorithm(GRPC_COMPRESS_DEFLATE); std::string prefix(Hello ); reply-set_message(prefix request-name()); return Status::OK; } };接口声明于 server_context.h底层由ServerContextBase::set_compression_algorithm提供。压缩算法与压缩级别的枚举体系示例代码中反复出现的GRPC_COMPRESS_GZIP、GRPC_COMPRESS_DEFLATE来自 gRPC C 层公共头文件 compression.h具体枚举定义在 compression_types.htypedef enum { GRPC_COMPRESS_NONE 0, /* 不压缩 */ GRPC_COMPRESS_DEFLATE, /* DEFLATE */ GRPC_COMPRESS_GZIP, /* GZIP */ GRPC_COMPRESS_ALGORITHMS_COUNT } grpc_compression_algorithm;需要注意当前仓库截至本文所基于的代码版本只实现了三种取值NONEidentity即不压缩、DEFLATE、GZIP枚举中残留的TODO(ctiller): snappy注释表明 Snappy 属于规划中但尚未落地的算法算法在 wire 协议上的字符串名称可在 compression_internal.cc 中看到明确映射identity、deflate、gzip该名称与 HTTP/2 的grpc-encoding元数据一一对应与压缩算法配套的还有「压缩级别」枚举GRPC_COMPRESS_LEVEL_NONE/LOW/MED/HIGH见 compression_types.h。级别是对算法的抽象由实现方根据对端能力自动把级别映射为具体算法与参数例如 low 映射为 gzip -3、high 映射为 gzip -9。压缩在协议层的协商与错误语义如果只停留在 API 用法上你可能无法解释「为什么对端不支持的算法会导致特定错误码」。gRPC 压缩的完整行为规范记录在 doc/compression.md摘要如下。压缩发生在单条消息粒度gRPC 的压缩作用在单条 message 粒度message 的定义见线缆格式文档 PROTOCOL-HTTP2.md由消息头中的 Compressed-Flag 与grpc-encoding元数据描述。也正因如此示例中的set_compression_algorithm是针对每一次具体 RPC 设置的。配置时机与方式规范允许两种配置时机Channel 创建时指定默认压缩无 per-RPC 配置时生效响应/发送时通过上下文覆盖一元 RPC 用{Client,Server}Context设置算法流式 RPC 用{Client,Server}Writer且此时只能选择「禁用压缩」。对端能力协商与错误语义通信双方可以「不对称压缩」即响应方可以选择与请求不同的压缩算法甚至完全不压缩。协议层由此定义了几条关键规则客户端发来的消息若使用了服务端不支持的算法服务端返回状态UNIMPLEMENTED并在响应头grpc-accept-encoding中声明其支持的算法列表服务端发出的数据若使用了客户端不支持的算法客户端侧表现为INTERNAL错误对端可以选择不主动披露自己支持的全部编码但一旦收到了用未披露算法压缩的消息就必须在响应的grpc-accept-encoding头中补上该编码服务端若获知客户端不支持某算法依据客户端最近一次发来的grpc-accept-encoding头则应直接以未压缩形式发送消息。显式禁用压缩的意义规范明确指出显式禁用压缩后下一条消息必须原样发送这一机制用于防范 BEAST/CRIME 类压缩侧信道攻击一元与流式场景均适用。因此「不压缩」本身也是GRPC_COMPRESS_NONE作为一个正式枚举值存在的原因。deflate 的精确含义规范特别强调gRPC 语境下的deflate指的是zlib 容器结构RFC 1950 deflate 算法RFC 1951服务端与客户端都不得发送裸 deflate 数据。这也是实现中把deflate名称绑定到 zlib 结构而非裸 deflate 流的依据。构建与运行回到示例目录先执行完整构建同时生成代码与两个可执行文件$ makemake默认目标为system-check greeter_client greeter_server见 Makefile。system-check会校验环境中的protoc版本要求 3.0.0 或更新以及grpc_cpp_plugin是否在 PATH 中可用。先在一个终端启动服务端$ ./greeter_server服务端监听0.0.0.0:50051并打印Server listening on 0.0.0.0:50051再在另一个终端运行客户端$ ./greeter_client客户端向localhost:50051发起一次SayHello请求载荷在 Channel 默认 GZIP 下创建连接、又在本次调用中被覆盖为 DEFLATE 压缩发送收到响应后打印Greeter received: Hello world world world world常见问题与排查要点现象 1改动压缩算法后抓包看不出差别。先确认载荷是否足够大或足够冗余——极小的消息压缩后可能更大或基本不变。示例中客户端特意使用world world world world作为输入正是为了制造可压缩的冗余。现象 2调用返回UNIMPLEMENTED。说明本次请求使用的压缩算法不在服务端支持集内。请核对两端grpc_compression_algorithm的取值是否一致并观察服务端响应的grpc-accept-encoding头确认其支持列表。现象 3调用返回INTERNAL。往往是服务端发出的数据用了客户端不支持的编码。注意 gRPC 的压缩能力协商以grpc-accept-encoding元数据为载体跨版本部署时需保证两端 gRPC 版本与编译开关一致。现象 4默认压缩没生效。请确认没有在ClientContext/ServerContext/ Writer 层面做更高优先级的覆盖同时确认没有在 channel 参数中通过GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET禁用该算法——算法被禁用时即使设置了默认值也不会启用。小结通过 compression 示例 与源码交叉验证可以归纳出 gRPC C 压缩配置的完整心智模型层面客户端 API服务端 API作用域与优先级默认配置ChannelArguments::SetCompressionAlgorithmServerBuilder::SetDefaultCompressionAlgorithm作用于整个 Channel / Server优先级最低调用级覆盖ClientContext::set_compression_algorithmServerContext::set_compression_algorithm作用于单次 RPC覆盖默认值能力开关GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET等 channel 参数ServerBuilder::SetCompressionAlgorithmSupportStatus控制可用算法集独立于默认算法底层实现上客户端默认算法最终被写入 channel 参数grpc.default_compression_algorithm见 channel_arguments.cc算法枚举与线缆名称identity/deflate/gzip的映射集中于 compression_internal.cc完整的协议语义与安全注意事项则以 doc/compression.md 为准。想进一步验证行为可继续阅读 helloworld 示例 熟悉基础流程再回到本文尝试把示例中的 GZIP/DEFLATE 组合替换为GRPC_COMPRESS_NONE与GRPC_COMPRESS_GZIP观察错误码与压缩效果的变化。【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考