尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

gRPC “Chaotic Good“ Legacy Transport 源码指南:自定义帧格式与控制/数据面分离的传输实现

gRPC “Chaotic Good“ Legacy Transport 源码指南:自定义帧格式与控制/数据面分离的传输实现 gRPC Chaotic Good Legacy Transport 源码指南自定义帧格式与控制/数据面分离的传输实现【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc导读本文围绕 gRPC Core 中名为Chaotic Good的实验性传输层Transport的legacy旧版实现展开讲解其在整个 gRPC 传输体系中的定位、目录结构、核心类设计、自定义帧格式、控制面与数据面分离架构以及基于 Channel Args 的配置方式。读完本文你将掌握chaotic_good_legacy目录下各文件的职责划分、帧在控制通道与数据通道之间的路由规则、消息分块与重组机制并理解该实现为何被视为过时、以及它正被哪些新实现取代。Chaotic Good 传输的定位与现状Chaotic Good混乱善良是 gRPC 中一个实验性传输层实现名称源自《龙与地下城》阵营体系——正如 新版实现说明 中所写这个名字最初反映该传输对接收到的消息保证对齐alignment这一特性。与传统基于 HTTP/2 的 CHTTP2 传输不同它使用自定义帧格式并重度依赖 gRPC Core 的 Promise API 构建异步、非阻塞的数据通路。当前仓库中并存着两套实现chaotic_good_legacy本文主体位于 src/core/ext/transport/chaotic_good_legacy/AGENTS.md是较旧的实现目前仍是许多应用实际使用的默认实现但正在被新实现逐步替换chaotic_good新版位于 src/core/ext/transport/chaotic_good/AGENTS.md是新的实验性实现二者共享同一套帧格式的 protobuf 定义chaotic_good_frame.proto但架构与代码组织不同。legacy 实现被明确标注为obsolete已过时AGENTS.md建议新代码不要使用它应转向新版实现。但从源码研究角度看legacy 实现代码更完整、注释更丰富是理解 Chaotic Good 设计思想的最佳入口。目录结构与文件职责chaotic_good_legacy目录下的文件布局如下文件职责chaotic_good_transport.h主要传输入口定义核心类ChaoticGoodTransport承载帧的读写与分发client_transport.h /client_transport.cc客户端侧传输实现定义ChaoticGoodClientTransportserver_transport.h /server_transport.cc服务端侧传输实现定义ChaoticGoodServerTransportframe.h /frame.cc自定义帧格式的 C 表示各类帧的类定义、序列化/反序列化frame_header.h /frame_header.cc每个帧的 12 字节定长帧头类型、连接 ID、流 ID、载荷长度control_endpoint.h /control_endpoint.cc控制面端点缓冲并批量冲刷所有小的控制写入data_endpoints.h /data_endpoints.cc数据面端点集合管理多条数据连接的读写与读票ReadTicket机制config.h传输配置从 Channel Args 派生通过 Settings 帧与对端协商message_chunker.h大消息分块发送辅助message_reassembly.h接收端消息重组辅助pending_connection.h待建立的数据连接描述legacy_ztrace_collector.h帧级追踪ZTrace采集client/chaotic_good_connector.h /.cc客户端连接器server/chaotic_good_server.h /.cc服务端监听与连接接纳其中AGENTS.md点名的核心入口为chaotic_good_transport.h客户端与服务端传输分别由client_transport与server_transport承载帧格式由frame/frame_header定义。核心类ChaoticGoodTransportAGENTS.md指出的唯一Major Class是grpc_core::chaotic_good_legacy::ChaoticGoodTransport。从 chaotic_good_transport.h 的源码可以看到它的定义class ChaoticGoodTransport : public RefCountedChaoticGoodTransport, public channelz::DataSource { public: struct Options { uint32_t encode_alignment 64; // 编码发送对齐字节数 uint32_t decode_alignment 64; // 解码接收对齐字节数 uint32_t inlined_payload_size_threshold 8 * 1024; // 内联载荷阈值8 KiB }; ... };它继承自RefCounted引用计数管理生命周期同时实现channelz::DataSource接口以向 channelz 导出transport_optionsencode_alignment、decode_alignment、inlined_payload_size_threshold等可观测数据见 AddData 实现。ChaoticGoodTransport内部组合了两个关键成员ControlEndpoint control_endpoint_控制面端点DataEndpoints data_endpoints_数据面端点集合。WriteFrame一条帧如何决定走控制通道还是数据通道WriteFrame 是发送路径的核心其路由逻辑如下如果没有可用数据端点或者载荷长度不超过inlined_payload_size_threshold默认 8 KiB则把帧头12 字节 载荷一并写到控制端点否则把载荷单独发往数据连接写入前按encode_alignment计算并补齐 padding随后再把帧头发送到控制端点并回填真实的数据连接 IDpayload_connection_id connection_id 1。这里的关键设计是小帧元数据、设置、取消等与控制帧头都走控制连接大载荷走数据连接。控制面与数据面分离使数据连接可以在载荷间自由调度避免控制帧被大消息的头部阻塞head of line blocking。ReadFrameBytes接收路径的反向路由ReadFrameBytes 先从控制端点读取 12 字节帧头并Parse然后依据payload_connection_id分支payload_connection_id 0载荷就在控制连接上立即读取并包装为IncomingFrame否则向对应数据端点发起读取得到一个读票ReadTicket调用方可在稍后异步Await()这些字节。接收端的读票机制很巧妙IncomingFrame的载荷可以是已就绪的 SliceBuffer或数据端点的读票二者之一IncomingFrame 定义这样即使从不同数据连接乱序拉取字节重组逻辑依然简单。收发主循环客户端与服务端共享同一套发送主循环模板 TransportWriteLoop从LockBasedMpscReceiver取出待发帧 →WriteFrame序列化写出 → 循环继续写失败则由TrySeq捕获并退出循环。这体现了该传输以 Promise 组合子Loop、Seq、TrySeq、If编排异步逻辑的编程范式。自定义帧格式12 字节帧头与九种帧类型帧头结构frame_header.h 定义了定长12 字节的帧头字段类型含义typeFrameTypeuint8帧类型payload_connection_iduint16载荷所在连接 ID0 表示控制连接stream_iduint32gRPC 流 IDpayload_lengthuint32载荷字节长度kFrameHeaderSize 12帧头提供Parse从 12 字节解析与Serialize写入 12 字节两个静态/成员方法。值得注意的细节是Padding(alignment)当payload_connection_id 0时不需要 padding控制连接上的内联载荷不做对齐只有走数据连接的载荷才需要按对齐字节数补齐这正是Chaotic Good名称中对齐含义的体现。九种帧类型FrameType 枚举 定义了完整帧类型集合源码注释提醒新增帧类型需同步更新frame_fuzzer.cc帧类型取值方向/用途kSettings0x00传输级设置协商客户端与服务端交换对齐、分块等能力kClientInitialMetadata0x80客户端初始元数据kClientEndOfStream0x81客户端流结束kServerInitialMetadata0x91服务端初始元数据kServerTrailingMetadata0x92服务端尾随元数据kMessage0xa0完整消息不分块kBeginMessage0xa1分块消息的开始标记含总长度kMessageChunk0xa2分块消息的载荷分片kCancel0xff取消帧的 C 类型体系frame.h 定义了抽象基类FrameInterface其虚方法构成所有帧的契约Deserialize(header, payload)从帧头载荷反序列化MakeHeader()由帧内容构造帧头SerializePayload(SliceBuffer)把载荷序列化进 SliceBufferToString()可读的调试字符串。在基类之上有四个通用模板ProtoTransportFrameFrameType, Body传输级stream_id 恒为 0的 protobuf 体帧如SettingsFrameProtoStreamFrameFrameType, Body流级 protobuf 体帧如ClientInitialMetadataFrame、ServerInitialMetadataFrame、ServerTrailingMetadataFrame、BeginMessageFrameEmptyStreamFrameFrameType无载荷的空帧如ClientEndOfStream、CancelFrame特殊帧MessageFrame完整消息与MessageChunkFrame分块消息二者承载MessageHandle/SliceBuffer载荷。最终的客户端帧类型集合为ClientFramestd::variant初始元数据、消息、开始消息、消息分片、结束流、取消服务端为ServerFrame初始元数据、消息、开始消息、消息分片、尾随元数据。值得注意CancelFrame仅出现在客户端帧集合中服务端通过其他方式发送取消。帧体使用 protobufchaotic_good_frame::ClientMetadata、chaotic_good_frame::ServerMetadata、chaotic_good_frame::Settings等仓库中对应定义位于 src/core/ext/transport/chaotic_good/chaotic_good_frame.protolegacy 实现通过 include 引用同一份 proto。控制面与数据面两条通道的工程实现ControlEndpoint批量冲刷的控制写入control_endpoint.h 将PromiseEndpoint包装为ControlEndpoint其核心是一个带容量上限的写缓冲所有小写入先进入Buffer::Queue由独立的write_party_一次性批量冲刷到网络上party wakeups are sticky因此能聚合几乎所有传输写入然后一次性 flush。缓冲上限MaxQueued()为1 MiB达到上限时Queue会挂起等待队列清空防止无限缓冲。读取侧ReadSlice/Read则是底层端点的直通passthrough仅附加CONTROL_CHANNEL: 错误前缀与追踪记录。DataEndpoints多数据连接的读写调度data_endpoints.h 实现了数据面。内部由三部分组成OutputBuffers所有数据连接的输出缓冲集合Write返回一个 Promise解析后得到被选中的数据连接 ID注意内部连接 ID 是 0 基线上传输时为 1 基以便为控制连接保留 0InputQueues输入读请求队列Read返回ReadTicketAwait()可异步取回字节。其设计要点是解耦读请求与读取完成——即使某个调用取消了读取也不会破坏其他调用的数据一致性见ReadTicket析构中的CancelTicketEndpoint每个数据连接一个内部各跑一个Party的读写循环WriteLoop/ReadLoop。DataEndpoints::empty()通过ReadyEndpoints() 0判断是否没有可用数据连接这直接影响WriteFrame的路由决策。客户端与服务端传输ChaoticGoodClientTransport 继承ClientTransportGetTransportName()返回chaotic_good。它维护StreamMapstream_id → Stream、LockBasedMpscReceiverClientFrame出站队列注释说明缓冲上限为 4保证每次流写入最多排队 2 帧、MessageChunker与MessageReassembly。MakeStream分配递增的next_stream_id_DispatchFrame按帧类型将服务端帧推入对应 callPushFrameIntoCall各重载。ChaoticGoodServerTransport 继承ServerTransport结构与客户端镜像SendCallInitialMetadataAndBody/SendCallBody/CallOutboundLoop处理出站TransportReadLoop/ReadOneFrame/ReadFrameBody处理入站NewStream根据客户端初始元数据帧创建流SendCancel发送取消。配置参数Channel Args 与 Settings 帧协商legacy 实现的大多数配置从 Channel Args 派生再通过 Settings 帧与对端交换形成客户端与服务端共享的最终配置config.h 的注释明确说明了这一点。可用的 Channel Args 宏定义于 config.hChannel Arg默认值说明grpc.chaotic_good.alignment64解码接收对齐字节数经 Settings 帧传播后同时成为对端编码对齐grpc.chaotic_good.max_recv_chunk_size1024 * 10241 MiB接收分块大小上限0 表示不分块grpc.chaotic_good.max_send_chunk_size1024 * 10241 MiB发送分块大小上限0 表示不分块grpc.chaotic_good.inlined_payload_size_threshold8 * 10248 KiB载荷内联阈值小于等于该值的载荷直接走控制连接grpc.tcp_tracing_enabledGRPC_ARG_TCP_TRACING_ENABLEDfalse是否启用 TCP/传输追踪几个值得注意的推导规则Config构造函数decode_alignment_最小为 1若max_recv_chunk_size_或max_send_chunk_size_任一为 0则两者都置 0关闭分块默认支持的特性为Settings::CHUNKING分块。协商过程通过 Settings 帧完成Config中的方法清晰地呈现了握手语义PrepareClientOutgoingSettings客户端直接把自己从 Channel Args 得到的结果发出CHECK_EQ(pending_data_endpoints_.size(), 0u)客户端不允许携带连接 IDPrepareServerOutgoingSettings服务端在收到客户端设置后把自己接纳的数据连接 ID加入设置帧再回发ReceiveServerIncomingSettings客户端侧取双方支持特性的交集并根据服务端给出的connection_id列表通过connector.Connect(connection_id)建立数据连接ReceiveClientIncomingSettings服务端侧严格校验对端特性必须都在自己支持集合内否则返回InternalError(Unsupported feature present in chaotic-good handshake: ...)且客户端不能指定连接 IDReceiveIncomingSettings将对端alignment作为自己的编码对齐encode_alignment_max_send_chunk_size_ min(自身, 对端 max_chunk_size)若不支持分块则双方分块大小归零。协商完成后MakeTransportOptions()产出ChaoticGoodTransport::OptionsMakeMessageChunker()产出MessageChunker(max_send_chunk_size_, encode_alignment_)。消息分块与重组大消息的传输策略当消息大于协商出的max_chunk_size时MessageChunker 将其拆分为多个MessageChunkFrame发送先发一个BeginMessageFrame记录消息总长度随后循环调用PayloadChunker::NextChunk()逐个产出分片。分片算法有两个细节当剩余量不足两倍块大小时最后两块会被切成近似等大以简化后续负载均衡避免极小的拖尾分片首个分片尽量保持对齐长度alignment对齐从而避免不必要的 padding 与拷贝。接收侧由MessageReassemblymessage_reassembly.h负责依据BeginMessageFrame的总长度重组分片客户端与服务端的Stream结构中都持有一个MessageReassembly实例。从 Legacy 到新版迁移路线图AGENTS.md明确指出 legacy 实现已过时正被迁移到 chaotic_good 目录下的新实现。新版实现的核心概念同样适用理解 legacy包括自定义帧格式由chaotic_good_frame.proto定义简单高效完整支持 gRPC 的头、消息、尾元数据Promise 架构重度基于 gRPC Core Promise API参见 src/core/lib/promise/AGENTS.md实现更异步、非阻塞控制面/数据面分离控制面头、尾元数据与数据面消息载荷分离控制面可用 TCP 等可靠传输数据面理论上可适配 UDP 等不同可靠性的通道。从源码结构看legacy 与新版的差异主要在于代码组织方式新版将入口拆为chaotic_good.h/.cc、client_transport、server_transport并引入frame_transport.h帧传输接口与scheduler.hPromise 调度器而 legacy 则将主要逻辑集中于chaotic_good_transport.h的ChaoticGoodTransport类。两版共享chaotic_good_frame.proto意味着帧格式的线上兼容基础一致。源码阅读路线建议若想深入理解 legacy 实现推荐按以下顺序阅读AGENTS.md先把握整体定位已过时、默认实现、迁移中chaotic_good_transport.h核心类的WriteFrame/ReadFrameBytes/TransportWriteLoop理解帧路由frame_header.h 与 frame.h帧格式与帧类型体系control_endpoint.h 与 data_endpoints.h控制面/数据面的工程实现config.h配置派生与 Settings 协商client_transport.h 与 server_transport.h两端如何编排这些部件。使用提醒当前仓库中该实现仍标记为实验性且已过时若在实际项目中评估使用应关注新版chaotic_good实现的进展并注意grpc.chaotic_good.*系列 Channel Args 的配置与协商语义可能随实现迁移而调整。【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表