
Wazuh inventory-sync 基准压测发送端FlatBuffers 消息协议完整参考【免费下载链接】wazuhWazuh - The Open Source Security Platform. Unified XDR and SIEM protection for endpoints and cloud workloads.项目地址: https://gitcode.com/GitHub_Trending/wa/wazuh导读本文是 Wazuh inventory-sync 基准压测工具Go 版tool_simulator/benchmark_sender设计文档系列的第 06 篇完整解析该工具在 wire 层之上承载业务数据的FlatBuffersMessage联合体协议从.fbs模式文件中的枚举与表定义到每种消息类型的构建顺序builder reference与入站解析路由parser reference再到StartAck的 FIFO 配对约束、常见构建陷阱与跨语言字节级一致性验证方法。读完本文你将能够基于仓库中的inventorySync.fbs自行生成 Go/Python 桩代码按规范构造Start、DataValue、DataBatch、End等全部消息并正确处理来自 manager 的StartAck、EndAck与ReqRet。1. 消息层在整个链路中的位置在 05-wire-protocol.md 描述的帧栈中最内层载荷即本协议identifier_blob s: || module_id || : || flatbuffer_bytes其中flatbuffer_bytes就是一个 FlatBufferMessage联合体union。该设计参考文档是发送端sender逐消息类型的权威引用其配套源码位于模式文件单一事实来源inventorySync.fbsGo 构建/解析实现internal/fbbuild/builders.go生成的桩代码internal/fb/Wazuh/SyncSchema/整体设计文档索引见 00-index.md其中 01 概述了该发送端的定位与 I/O 契约。2. Schema 摘要枚举与顶层联合体2.1 枚举Enums协议定义了 5 个枚举全部为ubyte无符号单字节宽度其数值一经发布即保持稳定枚举值十进制语义ModeModuleFull0, ModuleDelta1, ModuleCheck2, MetadataDelta3, MetadataCheck4, GroupDelta5, GroupCheck6同步模式模块全量/增量/校验、元数据增量/校验、组增量/校验OperationUpsert0, Delete1单条数据操作类型StatusOk0, Error1, Offline2, ChecksumMismatch3, Processing4会话/校验状态OptionSync0, VDFirst1, VDSync2会话选项漏洞数据相关MessageTypeNONE0, Start1, StartAck2, End3, EndAck4, DataValue5, DataBatch6, DataClean7, ChecksumModule8, DataContext9, ReqRet10联合体判别器稳定性约束数值是线上的稳定契约——不得重排、不得插入新值于中间。仓库中的真实模式文件 inventorySync.fbs 与文档一致使用byte声明等价。2.2 顶层联合体table Message { content : MessageContent; // FlatBuffer union — gives a (type, value) } union MessageContent { Start, StartAck, End, EndAck, DataValue, DataBatch, DataClean, ChecksumModule, DataContext, ReqRet }解析入站缓冲区时必须基于Message.content_type()一个MessageType值分支分发。Go 实现中对应fb.GetRootAsMessage(buf, 0)msg.ContentType()见 builders.go。3. 表定义逐字段参考以下字段顺序即模式文件中的声明顺序schema order依赖 schema 顺序的解析器如依赖 vtable 默认值跳过机制的老式解析器对此敏感禁止增删字段、禁止重排表字段按 schema 顺序要点Startagent:string; module:string; mode:Mode; size:uint64; option:Option; indices:[string]size为发送端将要发射的DataValue总条数indices为本次会话可能触及的 OpenSearch 索引列表StartAcksession:uint64; status:Statussession为 manager 分配的会话 ID被Offline拒绝时置UINT64_MAXEndsession:uint64会话结束标记EndAcksession:uint64; status:Status对End的确认DataValuesession:uint64; seq:uint64; operation:Operation; id:string; index:string; data:stringseq在会话内单调递增id为 OpenSearch 文档 IDindex必须出现在Start.indices中data为 UTF-8 JSON 文本DataBatchsession:uint64; items:[DataValue]1..N 条值序列化后大小必须 ≤ ~60 KBDataCleansession:uint64; indices:[string]清除 agent 拥有文档的索引列表ChecksumModulesession:uint64; module:string; checksum:stringchecksum为 40 位十六进制 sha1Pairkey:string; value:string上下文键值对DataContextsession:uint64; entries:[Pair]会话上下文ReqRetsession:uint64; ranges:[Range]缺失 seq 区间Range{first,last}均为闭区间inclusive3.1 文档模式与仓库实际模式的差异说明从源码结构看仓库中实际的 inventorySync.fbs 与上述“设计参考”存在两处需要注意的差异DataValue.data在真实模式中是[byte]字节向量且额外带有version:ulong字段Go 发送端用b.CreateByteVector(data)写入见 builders.go。真实模式中Start还包含architecture、hostname、os*、agent*、groups、global_version、cluster_name、cluster_node等字段Go 的BuildStart显式设置了cluster_name因为自 wazuh/wazuh#37238 起remoted 会校验其与 manager 自身集群名一致缺失或不匹配的Start在到达 inventory_sync 前即被拒绝见 builders.go 注释。设计文档以简化模式描述协议本质真实实现以.fbs为准发送端应遵循“字段顺序一致、数值稳定”这一总原则。4. 每种消息类型的 Builder 参考发送端使用flatc --go inventorySync.fbs生成的 Go 桩代码随后import github.com/wazuh/.../inventorySync/InventorySync实际包路径以生成器在 engine 树下产出的为准。FlatBuffers 要求字符串/向量必须先于引用它们的表完成序列化因此构建顺序是硬性约束。4.1Start字段取值来源与默认值字段来源scenario/状态默认值agent注册enrolment获得的 agent_id恒设置modulestep 的module来自 dump 元数据或 kindn/amodestep 的sync_mode映射为Mode未设置时ModuleDeltasizedump 取len(items)kind 取data_size恒设置optionstep 的option映射为OptionSyncindicesdump 元数据indices或[step.index]恒设置构建顺序序列化全部字符串agent、module、indices每个条目→ 序列化indices向量 → 开始Start表 → 填充标量与偏移量 → 结束表 → 包装进Message联合体。Go 参考实现BuildStart位于 builders.go其中向量按逆序PrependUOffsetT填充for i : len(idxOffsets)-1; i 0; i--这是 FlatBuffers 的强制要求。4.2End字段来源session与匹配的StartAck中的 id 一致对应BuildEnd(session)builders.go。4.3DataValue单条消息形式字段来源sessionStartAck中的 idseqrunner 本地计数器从 0 起、逐条递增operation合成数据用Upsertdump 用其operation字符串字段iddump 条目或生成fmt.Sprintf(doc-%d, seq)index逐条索引尊重多索引 dumpdatajson.Marshal(item.data)序列化为 UTF-8 字符串⚠️关键点data字段是string 而非 bytes——它承载的是 JSON 文本。实现见BuildDataValuebuilders.go。4.4DataBatchuse_databatchtrue时使用字段来源sessionStartAck中的 iditemsDataValue表向量每条同上分批规则持续向当前批次装入条目直到下一条会使序列化批次大小超过60 KB然后收尾当前批次并开启新批次。允许单条独立成批空批次永不发送。实用实现const BatchTargetBytes 60 * 1024 // 追加一条后计算 *当前* builder 大小若超过目标 // 收尾本批次并在下一条之前开启新批次。Go 参考实现BuildDataBatchbuilders.go演示了完整的“先逐条建DataValue表 → 逆序装向量 → 建DataBatch表”流程。4.5DataClean字段来源sessionStartAck中的 idindicesdump 元数据indices或 step 的index对应BuildDataClean(session, seq, index)builders.go。4.6ChecksumModule字段来源sessionStartAck中的 idmodulestep 的modulechecksumstep 的modulecheck_checksum40 个十六进制字符对应BuildChecksumModulebuilders.go。4.7DataContext字段来源sessionStartAck中的 identriesdump 元数据metadata.context若存在的Pair{key,value}向量否则[]当前 Python 实现仅在 dump 显式提供 context 块时才发射DataContext发送端应保持一致。4.8 联合体收尾包装所有*End返回一个 offset联合体包装必须是MessageStart→MessageAddContentMessageAddContentType→MessageEnd→b.Finish(msg)。忘记设置 type 会导致接收端看到MessageType_NONE而丢弃该帧。Go 的统一实现见wrapMessagebuilders.go。5. Parser 参考入站发送端必须处理的入站Message类型及路由规则MessageType读取字段路由StartAcksession,status匹配每个 agent 的 per-agent FIFO 中最早未决的 StartEndAcksession,status按sessionid 路由到对应 runnerReqRetsession,ranges[].first/last按sessionid 路由到 runner展开为一组 seq id其余所有类型Start、End、DataValue…不应来自 manager一旦收到记录日志并丢弃。Go 侧解析统一入口为ParseInboundbuilders.go它把结果归一化为Inbound{Type, Status, Session, Ranges}。值得注意的是该实现用recover()捕获 FlatBuffer 库在截断/损坏输入上的 panic 并翻译为哨兵错误保证 reader goroutine 面对线上垃圾数据也能存活。5.1 StartAck FIFO 排序约束FR-20manager 按收到Start的顺序分配sessionid。但 reader不能仅凭 StartAck 中的 session 找到 runner——StartAck 到达的瞬间原 runner 还不知道自己的 session id。因此 reader 必须per-agent FIFO queue of pending Start runners. on StartAck arrival: pop the FRONT of the queue resolve its Start future with (sessionack.session, statusack.status) on EndAck/ReqRet: look up the runner by session id (it now knows its id)若 StartAck 到达时 FIFO 为空记录告警并丢弃。该约束在仓库中有独立单测覆盖internal/agent/fifo_test.go。6. Builder 常见陷阱字符串长度data字符串是 UTF-8——切勿把原始二进制直接编码为 Gostring仅当源数据已是合法 UTF-8 时才使用flatbuffers.Builder.CreateByteString。向量顺序FlatBuffers 要求用StartVector(elem_size, count, alignment)建向量并以逆序压入元素。请使用生成的*StartFieldVector辅助函数不要手写。表终止每个*End返回 offset联合体包装必须设置 type见 4.8漏设 type 等于废帧。Builder 复用同一 goroutine 复用 builder 构造下一条消息前必须调用builder.Clear()Python/builder.Reset()Go。7. 验证跨语言一致性parity往返round-trip测试方法用完全相同的输入分别在 Python 与 Go 中构建同一条消息导出builder.Output()字节——两者必须逐字节一致任何差异都意味着字段顺序或对齐 bug。该原则在仓库中有两个层面的落地FlatBuffer 层fbbuild_test.go 验证Start/End/DataValue/DataBatch的构建与回读以及模拟 manager 侧StartAck、ReqRet的解析含多区间[1,3]、[7,9]场景size_test.go 专门验证Start.size在{0,1,17,100,1000,731,1447}多组取值下编码正确。wire 层internal/wire/parity_test.go 使用与 Python 捕获相同的常量输入做语义等价验证NFR-2并确认帧头!001!#AES:与 Python 逐字节一致、二进制载荷s:syscollector_sync:前缀往返无损。需要说明的是wire 层的 zlib 流在 Gocompress/zlib与 Pythonlibz 封装之间原始字节天然不同但两者都是合法 deflate 流manager 只看到解压后的内容——因此 wire 层验收标准是“语义等价”而 FlatBuffer 层必须是“逐字节一致”。8. 小结06-flatbuffers-messages.md所定义的这套协议是 Go 压测发送端与真实 wazuh-manager 之间 inventory-sync 业务的“心跳”。把握三条主线即可正确实现数值与字段顺序是不可变契约枚举值、schema order构建必须遵循 FlatBuffers 的顺序约束先字符串/向量、逆序压栈、联合体必设 type入站只信任三种类型StartAck/EndAck/ReqRet其中StartAck走 per-agent FIFO 配对其余按 session 路由。实际落地时以仓库的 inventorySync.fbs 为准生成桩代码并以 fbbuild 包 的构建器与测试作为 Go 侧的规范实现参照。【免费下载链接】wazuhWazuh - The Open Source Security Platform. Unified XDR and SIEM protection for endpoints and cloud workloads.项目地址: https://gitcode.com/GitHub_Trending/wa/wazuh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考