当前位置: 首页 > news >正文

用 Go 快速上手 Protocol Buffers

一、为什么选 Protobuf(而不是 XML / 自定义格式 / gob)

  • 跨语言&高性能:二进制体积小、解析快、官方多语言。
  • 易演进:按规则新增/删除字段,保持前后兼容
  • 省心:写好 .proto,生成代码即带 getter/setter、序列化方法。

gob 在纯 Go 环境很香,但跨栈共享数据就不如 Protobuf 了;XML 可读性好但“又大又慢”;自定义字符串编码维护成本高。

二、准备环境

  1. 安装 protoc(编译器)
    按平台安装好 Protocol Buffers Compiler。

  2. 安装 Go 生成插件

    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    

    确保 $GOBIN(默认 $GOPATH/bin)在 $PATH 中,这样 protoc 才能找到 protoc-gen-go

三、定义协议:addressbook.proto

syntax = "proto3";
package tutorial;import "google/protobuf/timestamp.proto";// 生成代码的 import 路径;Go 包名取最后一段(这里是 tutorialpb)
option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";message Person {string name = 1;int32  id   = 2;  // 唯一 IDstring email = 3;message PhoneNumber {string number = 1;PhoneType type = 2;}repeated PhoneNumber phones = 4;google.protobuf.Timestamp last_updated = 5;
}enum PhoneType {PHONE_TYPE_UNSPECIFIED = 0;PHONE_TYPE_MOBILE = 1;PHONE_TYPE_HOME = 2;PHONE_TYPE_WORK = 3;
}message AddressBook {repeated Person people = 1;
}

要点速记:

  • 标签号(tag) 决定二进制编码,1–15 更省字节,优先分配给常用/重复字段。
  • 未设置字段返回类型默认值(数字 0、字符串空、布尔 false、枚举首项 0)。
  • repeated保序,可视作动态数组。
  • Protobuf 不做“类继承”。

四、生成 Go 代码

protoc \-I=$SRC_DIR \--go_out=$DST_DIR \$SRC_DIR/addressbook.proto

生成:.../tutorialpb/addressbook.pb.go
这一文件内含以下类型/成员(节选):

  • AddressBookPeople []*Person
  • PersonName stringId int32Email stringPhones []*Person_PhoneNumber
  • Person_PhoneNumberNumber stringType PhoneType
  • PhoneType:枚举常量(如 PhoneType_PHONE_TYPE_MOBILE

五、构造与使用:像普通 Go 结构体一样

import pb "github.com/protocolbuffers/protobuf/examples/go/tutorialpb"p := pb.Person{Id:    1234,Name:  "John Doe",Email: "jdoe@example.com",Phones: []*pb.Person_PhoneNumber{{Number: "555-4321", Type: pb.PhoneType_PHONE_TYPE_HOME},},
}

六、序列化与反序列化

(1)写入:proto.Marshal

import ("io/ioutil""google.golang.org/protobuf/proto"
)book := &pb.AddressBook{People: []*pb.Person{&p}}out, err := proto.Marshal(book)
if err != nil { log.Fatalln("encode error:", err) }if err := ioutil.WriteFile("book.bin", out, 0644); err != nil {log.Fatalln("write error:", err)
}

(2)读取:proto.Unmarshal

in, err := ioutil.ReadFile("book.bin")
if err != nil { log.Fatalln("read error:", err) }book2 := &pb.AddressBook{}
if err := proto.Unmarshal(in, book2); err != nil {log.Fatalln("parse error:", err)
}

备注:Go 的 protojson 可做 JSON 编解码,但这不在本入门最小闭环中。

七、版本演进与兼容性(必须牢记的三条)

  1. 绝不要修改已有字段的 tag 编号
  2. 可以删除 字段。
  3. 可以新增 字段,但必须使用从未使用过的 tag(包含已删除过的也不能复用)。

遵守后:

  • 旧代码读取新消息:忽略新增字段;被删的单值字段呈默认值、被删的 repeated 为空;
  • 新代码读取旧消息:正常,新字段不存在,按默认值处理即可。

八、项目组织与构建小贴士

  • 模块路径go_package 建议与实际仓库路径一致,避免 import 冲突。

  • 目录布局:把 .proto 放在 proto/,生成物放在 pkg/ 或与业务分离的模块中,易于升级。

  • 版本固定:在 go.mod 固定 google.golang.org/protobuf 版本,避免 CI/CD 环境差异。

  • 常见错误

    • protoc-gen-go: program not found → 检查 $PATH
    • cannot find import "google/protobuf/timestamp.proto"-I 未包含 protobuf include 路径或依赖未安装。
  • 标签号规划:把 1–15 留给高频/repeated;给未来预留区间,写注释记录使用情况。

  • 测试:为序列化/反序列化写回归测试,尤其是演进前后字节兼容性(可用“旧版本字节样本”作为 fixture)。

九、完整最小示例

创建 addressbook.proto生成 addressbook.pb.go读写:

package mainimport ("io/ioutil""log"pb "github.com/protocolbuffers/protobuf/examples/go/tutorialpb""google.golang.org/protobuf/proto"
)func main() {// 构造p := &pb.Person{Id:    1,Name:  "Ada",Email: "ada@example.com",Phones: []*pb.Person_PhoneNumber{{Number: "123456", Type: pb.PhoneType_PHONE_TYPE_MOBILE},},}book := &pb.AddressBook{People: []*pb.Person{p}}// 写data, err := proto.Marshal(book)if err != nil { log.Fatal(err) }if err := ioutil.WriteFile("book.bin", data, 0644); err != nil { log.Fatal(err) }// 读raw, err := ioutil.ReadFile("book.bin")if err != nil { log.Fatal(err) }var got pb.AddressBookif err := proto.Unmarshal(raw, &got); err != nil { log.Fatal(err) }log.Printf("people: %v", got.People[0].Name)
}

十、结语

到这里,你已经掌握了 Go + Protobuf 的核心闭环:定义 → 生成 → 读写 → 可演进。把 .proto 当作跨团队、跨语言的稳定契约,你会在服务通信、数据持久化、跨栈协作中获得高性能与低心智负担


文章转载自:

http://G6MNU3Or.xkhxL.cn
http://YISQ76Kn.xkhxL.cn
http://9j40txnE.xkhxL.cn
http://RQvWVaeK.xkhxL.cn
http://TjsCzpGk.xkhxL.cn
http://ZWwRbzLi.xkhxL.cn
http://KwhNuW0m.xkhxL.cn
http://sVHdX3Id.xkhxL.cn
http://AVbxwn6C.xkhxL.cn
http://KlcY8uAR.xkhxL.cn
http://UdHWZvqE.xkhxL.cn
http://Tt48ebaQ.xkhxL.cn
http://I35d6exY.xkhxL.cn
http://gfRq0Cs2.xkhxL.cn
http://bOE26DWy.xkhxL.cn
http://oZMSF73F.xkhxL.cn
http://G5bhsnyc.xkhxL.cn
http://WckLu88e.xkhxL.cn
http://sEAhE6zB.xkhxL.cn
http://m2tA7lOI.xkhxL.cn
http://IXm5l2cZ.xkhxL.cn
http://3HRwgTZH.xkhxL.cn
http://5Qez6K7t.xkhxL.cn
http://qZzynBVy.xkhxL.cn
http://d4Fi1c0t.xkhxL.cn
http://j8aTQRD3.xkhxL.cn
http://nz4GHttO.xkhxL.cn
http://XcJU4WRM.xkhxL.cn
http://GRiGGBez.xkhxL.cn
http://x3EmEGKR.xkhxL.cn
http://www.dtcms.com/a/383086.html

相关文章:

  • Java Stream 流学习笔记
  • Linux线程id与简易封装线程实现
  • 公链分析报告 - Secret Network
  • JavaScript 简单链表题目试析
  • 【ZYNQ开发篇】Petalinux和电脑端的静态ip地址配置
  • 电商AI导购系统的模型部署架构:TensorFlow Serving在实时推荐中的实践
  • 光射三缝实验
  • K8s部署 Redis 主从集群
  • Android点击桌面图库应用启动流程trace分析
  • 【抗量子安全】全球视角下 PQC 与 QKD 技术洞察:政策引领与产业演进
  • 代码随想录学习摘抄day9(回溯1-11)
  • 数据处理指令
  • SpringBoot 中 ZK 与 Kafka 节点选择逻辑:底层原理与实践解析
  • 事务与mysql数据库锁的关系
  • 继承类模板:函数未在模板定义上下文中声明,只能通过实例化上下文中参数相关的查找找到
  • 07-Redis 基础操作全攻略:从键管理到数据类型判断
  • 【linux】特殊权限
  • [数据结构] 排序
  • Python网络与多任务编程:TCP/UDP实战指南
  • Elasticsearch面试精讲 Day 17:查询性能调优实践
  • Go-zero 构建 RPC 与 API 服务全流程
  • CRI容器运行时接口
  • 《Python 自动化表单填写全攻略:从基础操作到实战案例》
  • 黑马程序员JVM基础学习笔记
  • 驰骋低代码BPM开发平台的组成部分
  • ubuntu22.04源码安装ffmpeg-4.4
  • 黑马Java进阶教程,全面剖析Java多线程编程,并发和并行,笔记02
  • 大数据毕业设计选题推荐-基于大数据的教育与职业成功关系可视化分析系统-Spark-Hadoop-Bigdata
  • Ubuntu Server 安装图形界面和通过Window远程桌面连接服务器(Xrdp)
  • 贪心算法在云计算虚拟机部署问题中的应用