容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载这篇技术指南以 docker-ce 仓库内 vendored 的github.com/golang/protobuf组件为主体系统讲解 Go 语言中使用 Protocol Buffers 的完整链路从环境安装、protoc编译到.pb.go生成代码的使用再到proto运行时库的编解码原理与 gRPC 生成支持。读者读完可以掌握在本仓库以及任何 Go 项目中编写.proto、生成并调用 protobuf 代码的完整实战方案并理解 docker-ce 内部大量 protobuf 生成代码如 libnetwork、API 类型、插件通信背后的生成与兼容机制。关联文档位于 components/engine/vendor/github.com/golang/protobuf/README.md其描述的内容由同一 vendor 目录下的 proto/ 运行时库与 protoc-gen-go/ 生成器源码直接支撑。docker-ce 在vendor.conf中锁定该组件版本为v1.3.5components/engine/vendor.confgo.mod 声明其要求 Go 1.9 及以上go.mod。本文所有命令与代码均可直接对照仓库验证。一、为什么 docker-ce 会内置 protobuf仓库内的真实使用场景Protocol Buffers简称 protobuf是 Google 设计的二进制数据交换格式比 JSON/XML 更紧凑、解析更快并具备跨语言 Schema 定义能力。docker-ce 引擎内部大量模块用它描述结构化数据仓库里可以随处看到由protoc生成的.pb.go文件例如components/engine/api/types/swarm/runtime/plugin.pb.goSwarm 运行时插件的请求/响应数据结构components/engine/builder/remotecontext/tarsum.pb.go构建上下文tarsum的数据摘要结构components/engine/libnetwork/agent.pb.go、overlay.pb.go、networkdb.pb.golibnetwork 网络代理与 gossip 同步数据的序列化格式以及 vendor 目录下 containerd、etcd、hcsshim 等上游依赖携带的各类.pb.go。也就是说docker-ce 把golang/protobuf作为引擎自身的依赖随源码一起 vendored本文介绍的安装、编译与运行时 API 正是这些生成文件能够正常工作被proto.Marshal/proto.Unmarshal编解码、被 gRPC 客户端/服务端使用的基础。二、环境准备与安装从 protoc 到 protoc-gen-go文档明确指出要使用这套软件必须依次完成三件事安装 protocol buffers 标准 C 实现即protoc编译器安装 Go 编译工具链建议直接遵循 Go 官方安装说明使用 gccgo 的读者可参考对应的 gccgo 安装指引获取本仓库代码并安装proto包。最简单的方式是运行go get -u github.com/golang/protobuf/protoc-gen-go该命令会同时拉取依赖并将编译器插件protoc-gen-go安装到$GOPATH/bin若设置了$GOBIN则安装到$GOBIN。关键前提这个插件必须位于$PATH中protoc才能找到它否则编译.proto时会报“找不到插件”的错误。如果需要让protoc-gen-go的版本与proto包版本严格一致文档给出了基于 Git tag 的锁定方案GIT_TAGv1.2.0 # change as needed go get -d -u github.com/golang/protobuf/protoc-gen-go git -C $(go env GOPATH)/src/github.com/golang/protobuf checkout $GIT_TAG go install github.com/golang/protobuf/protoc-gen-go提示本文涉及的仓库环境已将版本锁定为 v1.3.5见 components/engine/vendor.conf实际使用时建议始终让生成器与运行时库来自同一提交。安装完成后的软件由两大部分组成协议编译器插件protoc-gen-go读取.proto描述输出可直接编译使用的 Go 源文件运行时库proto包为生成代码提供编码marshal、解码unmarshal与字段访问等运行期支持。文档特别说明该插件没有“插入点”insertion points即不支持通过插入点机制把自定义代码注入生成文件。三、核心使用流程两步走安装完成后使用流程分为两步编译.proto定义用protoc调用--go_out参数指定 Go 代码输出目录protoc --go_out. *.proto在程序中导入生成文件生成的 Go 代码会以import github.com/golang/protobuf/proto的方式引用运行时库随程序一起编译。生成的每个.proto源文件对应一个.pb.go文件把.proto后缀替换为.pb.go如foo.proto→foo.pb.go。源码中goFileName的实现印证了这一点protoc-gen-go/generator/generator.go它同时对.proto与.protodevel后缀做替换处理。四、包与输出路径go_package 与 paths 的完整规则protobuf 语言的“package”概念与 Go 的 package 概念并不能一一对应。生成代码中每个源.proto文件归属于一个 Go package其名称与导入路径通过go_package选项声明option go_package github.com/golang/protobuf/ptypes/any;若缺少go_package生成器会尝试推导包名与导入路径但强烈建议始终显式声明源.proto文件与生成的.pb.go文件一一对应但任意数量的.pb.go文件可以属于同一个 Go package。生成器解析go_package的完整逻辑在 generator.go 的 goPackageOption 函数若选项形如import/path;pkgname分号前为导入路径、分号后为包名若选项含/则/之前视为导入路径最右侧/之后作为包名否则整个选项仅作为包名不带导入路径。输出目录的两种选取方式以inputs/x.proto且go_package为github.com/golang/protobuf/p为例相对于导入路径默认pathsimportprotoc --go_out. inputs/x.proto # writes ./github.com/golang/protobuf/p/x.pb.go这种方式配合--go_out$GOPATH使用效果很好——生成结果直接落入$GOPATH下对应的导入路径目录。相对于输入文件pathssource_relativeprotoc --go_outpathssource_relative:. inputs/x.proto # generate ./inputs/x.pb.go当不指定paths参数时默认采用import模式。生成器在 参数解析代码 中校验paths只接受import与source_relative两个值其余值会直接触发Unknown path type失败。包名推导的优先级当文件未声明go_package时生成器按如下顺序确定包名generator.go同一包内其他文件的go_package选项历史兼容行为命令行import_pathquux/bar参数源文件顶部的package quux.bar;声明源文件名本身。同时生成器会检查同一批输入文件是否具有一致的包名与导入路径不一致则报错避免把不同包的代码混入同一输出目录。五、生成代码接口全解proto2 与 proto3 的行为差异文档给出了运行时库接口的完整属性清单这是理解 protobuf 生成代码的核心。proto包把 Go 数据结构与 protobuf 线格式互转与protoc-gen-go生成的 Go 代码协同工作。对于协议缓冲区变量v命名导出字段名从camel_case转为CamelCase后导出字段赋值v上没有设置字段的方法直接把字段当结构体字段赋值即可GetterGetFoo系列方法在字段未设置时返回该字段的默认值且接收者为 nil 消息时也能正常工作零值即正确初始状态struct 的零值即为正确的初始化状态marshal 前必须设置好所有期望的字段Reset()将 protobuf struct 恢复为零状态非 repeated 字段是指针nil 表示未设置例如 proto2 中optional int32 f生成F *int32repeated 字段是切片如repeated int64 reps生成[]int64字段设置辅助函数如msg.Foo proto.String(hello)取值辅助函数已被GetFoo取代并标记废弃默认值常量形如Default_StructName_FieldName因为 getter 已处理默认值直接使用这些常量应很罕见枚举生成类型名与“名字↔数值”映射表枚举值以枚举类型名为前缀枚举类型带String()方法和用于构造消息的Enum()方法嵌套 message/group/enum类型名以宿主 message 类型名为前缀扩展extension描述符名称以E_开头后跟下划线分隔的嵌套消息列表若有再拼接 CamelCase 后的扩展字段名配套HasExtension、ClearExtension、GetExtension、SetExtension函数oneof在 message 中生成单个字段每种可能的字段值对应一个区分的包装类型Marshal / Unmarshal编码与解码线格式的函数。当.proto文件声明syntaxproto3时有两个重要差异非 message 类型的非 repeated 字段生成值而非指针枚举类型不再生成Enum()方法。六、完整示例从 proto 定义到 Go 程序1. 定义 test.protosyntax proto2; package example; enum FOO { X 17; }; message Test { required string label 1; optional int32 type 2 [default77]; repeated int64 reps 3; }2. 编写并运行 Go 程序package main import ( log github.com/golang/protobuf/proto path/to/example ) func main() { test : example.Test{ Label: proto.String(hello), Type: proto.Int32(17), Reps: []int64{1, 2, 3}, } data, err : proto.Marshal(test) if err ! nil { log.Fatal(marshaling error: , err) } newTest : example.Test{} err proto.Unmarshal(data, newTest) if err ! nil { log.Fatal(unmarshaling error: , err) } // Now test and newTest contain the same data. if test.GetLabel() ! newTest.GetLabel() { log.Fatalf(data mismatch %q ! %q, test.GetLabel(), newTest.GetLabel()) } // etc. }这段程序演示了完整闭环用proto.String/proto.Int32辅助函数构造字段、用proto.Marshal编码为线格式字节、用proto.Unmarshal还原到新对象最后用生成的 getter 校验数据一致性。注意Type未显式设置时GetType()将返回default77定义的默认值label是required字段marshal 前必须设置。3. 运行时编解码的源码佐证proto.Marshal是编码的主入口proto/table_marshal.go其实现为生成代码预留了三层通道先检测newMarshaler接口现代生成代码通过XXX_Marshal走高效路径再回退到旧的Marshaler接口兼容旧版生成代码最后兜底使用InternalMessageInfo的反射式 marshal。解码入口proto.Unmarshal位于 proto/decode.go并提供了UnmarshalMerge用于把线格式数据合并追加到已存在的消息中。仓库里的真实生成代码完全符合上述模式例如 components/engine/api/types/plugins/logdriver/entry.pb.go 中的LogEntry字段声明带 protobuf 结构标签protobuf:bytes,1,opt,namesource,proto3自动生成Reset()、String()、ProtoMessage()、Descriptor()自动生成GetSource()、GetTimeNano()等 nil 安全的 getter。docker-ce 引擎加载容器日志插件时就是通过这份LogEntry的 marshal/unmarshal 在插件进程间传递日志行数据。该文件由 gogo/protobuf 生成包注释中protoc-gen-gogo其运行时依赖模式与 golang/protobuf 完全一致可相互印证。七、protoc-gen-go 参数详解向插件传参的方式是在--go_out中用冒号分隔“逗号分隔的参数列表”与“输出目录”protoc --go_outpluginsgrpc,import_pathmypackage:. *.proto各参数含义如下参数说明默认值 / 备注paths(import\|source_relative)指定生成文件的路径结构见上文“包与输出路径”一节默认importpluginsplugin1plugin2指定加载的子插件列表本仓库内置的唯一插件是grpcnone表示不加载任何插件generator.goMfoo/bar.protoquux/shme声明foo/bar.proto关联到 Go 包quux/shme受import_prefix参数影响annotate_codetrue为生成代码附加源码位置注释可选generator.go注意import_prefix与import_path已被标记为废弃不应再使用import_prefixxxx为所有 import 增加一个统一前缀import_pathfoo/bar在没有任何输入文件声明go_package时用作包名若它包含斜杠则最右侧斜杠之前的部分会被忽略。M前缀参数的底层实现在 generator.go 的参数解析循环 中凡是M开头的参数都被写入g.ImportMap从而在生成 import 语句时完成路径映射。八、gRPC 支持pluginsgrpc 生成服务代码当.proto文件声明了 RPC 服务时可以让protoc-gen-go生成与 gRPC 兼容的代码。做法是向--go_out传递plugins参数protoc --go_outpluginsgrpc:. *.protogRPC 生成代码作为二级插件实现源码位于 protoc-gen-go/grpc/grpc.go通过generator.RegisterPlugin注册generator.go。其生成的客户端代码引用google.golang.org/grpc、google.golang.org/grpc/codes与google.golang.org/grpc/status三个包grpc.go并像proto库一样声明grpc.SupportPackageIsVersionN常量来做版本断言。docker-ce 的 libnetwork 与 containerd 集成大量依赖这类 gRPC 风格的服务定义与通信。需要注意protoc-gen-go/generator暴露的插件接口是不受支持、可能无预告地发生不兼容变更的——gRPC 生成代码正是基于该接口实现因此它不在稳定性承诺范围内。九、版本兼容机制ProtoPackageIsVersionN 常量文档要求尽可能使用与proto运行时库同一提交构建的protoc-gen-go。这一约束通过版本常量机制强制执行。proto包在 proto/lib.go 中声明了包级常量const ( ProtoPackageIsVersion3 true ProtoPackageIsVersion2 true ProtoPackageIsVersion1 true )生成器写出的每个.pb.go文件第一段都会包含编译期断言generator.go// This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ proto.ProtoPackageIsVersion3 // please upgrade the proto package若运行时库版本过旧缺少对应版本常量整个生成代码文件会编译失败从而在构建期就暴露版本不匹配。仓库内大量生成文件都带有这种断言例如 protoc-gen-go/descriptor/descriptor.pb.go 与 ptypes/any/any.pb.go 均引用ProtoPackageIsVersion3。每次生成代码需要更新的库特性时生成器会在同一提交中递增版本号并声明新常量移除某个兼容常量属于破坏性变更需遵循下述公告政策。十、兼容性承诺与破坏性变更边界proto库与生成代码预期长期稳定但官方保留在以下场景无预告地做出破坏性变更的权利安全规范或实现出现必须通过破坏兼容性解决的安全问题未定义行为程序依赖 protobuf 规范中未定义的方面未来可能被破坏规范错误或变更修复规范的不一致、不完整或变更时可能影响既有程序的含义与合法性Bug 修复修复违反规范的 bug 时依赖该 bug 行为的程序可能受影响为生成 struct 增加方法或字段可能与 schema 中既有字段名冲突代码生成器会在冲突字段/方法名后追加下划线以避免编译错误对XXX前缀的生成字段/方法做增删改这些部分虽因必要而导出但不属于公共 API对生成代码中未导出符号的增删改。除此之外的破坏性变更会提前 6 个月通过protobufgooglegroups.com公告。观察docker-ce 中如何消费这些规则仓库内下游生成文件如 gogo/protobuf 生态同样遵循“包级常量断言 XXX_前缀字段 Reset/GetXxx生成方法”的契约。例如 components/engine/api/types/plugins/logdriver/entry.pb.go 使用const _ proto.GoGoProtoPackageIsVersion2做同构的编译期检查。理解这套规则有助于在升级 docker-ce 依赖或自行维护 protobuf 生成代码时预判哪些改动可能引发编译失败或行为变化。十一、配套子包一览完整模块地图vendor 目录下该组件包含以下子包读者可按需深入components/engine/vendor/github.com/golang/protobuf目录作用proto/运行时库marshal/unmarshal、extensions、text 格式、message set、clone/equal/discard、表驱动编解码table_marshal/table_unmarshal/table_mergeprotoc-gen-go/生成器主程序descriptor/ 为 .proto 文件描述符类型generator/ 为核心生成框架grpc/ 为 gRPC 二级插件jsonpb/protobuf 与 JSON 互转Marshaler/Unmarshalerptypes/标准 Well-Known Types 的 Go 实现与工具any、duration、empty、struct、timestamp以及TimestampProto、DurationProto、Any等转换函数其中ptypes.TimestampProto、ptypes.DurationProto等转换函数在 docker-ce vendor 的上游依赖如 cloud.google.com/go、containerd中被大量使用用于把 Go 的time.Time与 protobuf 时间戳互相转换。十二、常见问题与最佳实践小结protoc找不到protoc-gen-go确认插件已安装到$GOPATH/bin或$GOBIN且该目录在$PATH中。生成的.pb.go与运行时库版本不匹配优先让protoc-gen-go与proto包来自同一 Git 提交可参考第二节的GIT_TAG锁定流程遇到ProtoPackageIsVersionN断言失败时先升级proto包。始终显式声明go_package文档明确建议不要依赖生成器的自动推导显式声明才能保证导入路径与包名的确定性。默认路径是import模式若希望生成文件紧邻源.proto文件必须显式传pathssource_relative。proto3 与 proto2 的生成代码差异proto3 标量字段生成值而非指针且无Enum()辅助方法迁移 schema 语法时需同步调整赋值与判空逻辑。保持插件接口谨慎使用generator.Plugin接口与 gRPC 生成器不在稳定性承诺之内第三方插件需自行承担升级风险。充分利用配套包涉及 JSON 序列化或 Well-Known Types 时优先使用jsonpb与ptypes避免手写转换逻辑。通过本文可以确认docker-ce 的 libnetwork、Swarm runtime、日志插件等模块的 protobuf 生成代码均建立在golang/protobuf这套“生成器 运行时 版本断言”机制之上。掌握文中流程与参数即可在仓库内复现.proto→.pb.go→ marshal/unmarshal → gRPC 服务的完整技术闭环。赞分享容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载相关推荐docker-ce 中的 Go Protocol Buffers 支持库github.com/golang/protobuf 模块指南docker ce 中的 Go Protocol Buffers 支持库github.com/golang/protobuf 模块指南 github.com/容器运行时云原生推荐开源项目protoc-gen-gorm —— 简化Go语言中Protocol Buffers对象持久化的神器推荐开源项目protoc gen gorm —— 简化Go语言中Protocol Buffers对象持久化的神器 项目介绍 protoc gen gorm是一gogoprotobuf 在 docker-ce 中的应用Go 高性能 Protocol Buffers 代码生成实战指南gogoprotobuf 在 docker ce 中的应用Go 高性能 Protocol Buffers 代码生成实战指南 导读 gogoprotobuf 容器运行时云原生创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?