Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go语言,Protobuf 极速入门!

Go语言,Protobuf 极速入门!

作者头像
微客鸟窝
发布于 2021-11-12 03:21:52
发布于 2021-11-12 03:21:52
90300
代码可运行
举报
文章被收录于专栏:Go语言指北Go语言指北
运行总次数:0
代码可运行

Protobuf 是 Protocol Buffers 的简称,是一种与语言、平台无关,可扩展的序列化结构化数据的数据描述语言,Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。

基本语法

hello.proto 文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
syntax = "proto3";

package main;

message String {
    string value = 1;
}
  1. 第一行声明使用 proto3 语法。否则,默认使用 proto2 语法,目前主流推荐使用 v3 版本。此声明必须是文件的非空、非注释的第一行。
  2. package 指令指明当前是 main 包,用户也可以针对不同的语言定制对应的包路径和名称。
  3. message 关键字定义一个 String 类型消息体,在最终生成的Go语言代码中对应一个 String 结构体。每一个消息体的字段包含三个属性:类型、字段名称、字段编号。在消息体的定义上,除类型以外均不可重复。此处 String 类型中只有一个字符串类型的 value 成员,该成员编码时用1编号代替名字。
  4. Protobuf 中最基本的数据单元是 message,类似 Go 语言中的结构体。在 message 中可以嵌套 message 或其它的基础数据类型的成员。

关于标识号

消息体中字段定义了唯一的数字值。这些数字是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。

最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]的标识号, Protobuf 协议实现中对这些进行了预留。如果非要在 .proto 文件中使用这些预留标识号,编译时就会报警。类似地,你不能使用之前保留的任何标识符。

添加注释

.proto 文件添加注释,可以使用C/C++风格的 // 和 /* … */ 语法格式

保留字段

如果从前面定义的消息中删除了 和 字段,应保留其字段编号,使用关键字 reserved:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
syntax "proto3";

message Stock {
    reserved 3, 4;
    // ...
}

还可以将 reserved 关键字用作将来可能添加的字段的占位符。可以使用 to 关键字将连续字段号占位。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
syntax "proto3";

message Info {
    reserved 2, 9 to 11, 15;
    // ...
}

生成相应的Go代码

Protobuf 核心的工具集是 C++ 语言开发的,官方的 protoc 编译器中并不支持Go语言。要想基于上面 的 hello.proto 文件生成相应的Go代码,需要安装相应的插件。

  • 安装官方的 protoc 工具,可以从 https://github.com/google/protobuf/releases 下载。
  • 安装针对Go语言的代码生成插件,通过 go get github.com/golang/protobuf/protoc-gen-go 命令安装。

通过以下命令生成相应的Go代码:

$ protoc --go_out=. hello.proto

  • go_out 参数告知 protoc 编译器去加载对应的 protoc-gen-go 工具,生成的代码放到当前目录。最后是一系列要处理的protobuf文件的列表。
  • plugins=plugin1+plugin2:指定要加载的子插件列表,我们定义的 proto 文件是涉及了 RPC 服务的,而默认是不会生成 RPC 代码的,因此需要在 go_out 中给出 plugins 参数传递给 protoc-gen-go,告诉编译器,请支持 RPC(这里指定了内置的 grpc 插件)。

基本数据类型

protobuf 所生成出来的数据类型并非与原始的类型完全一致,下面是一些常见的类型映射:

生成的 hello.pb.go 文件

pb.go 文件是对 proto 文件所生成的对应的 Go 代码,在实际应用中将会引用到此文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello.proto

package main

import (
 fmt "fmt"
 proto "github.com/golang/protobuf/proto"
 math "math"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// 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

type String struct {
 Value                *String  `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
 XXX_NoUnkeyedLiteral struct{} `json:"-"`
 XXX_unrecognized     []byte   `json:"-"`
 XXX_sizecache        int32    `json:"-"`
}

func (m *String) Reset()         { *m = String{} }
func (m *String) String() string { return proto.CompactTextString(m) }
func (*String) ProtoMessage()    {}
func (*String) Descriptor() ([]byte, []int) {
 return fileDescriptor_61ef911816e0a8ce, []int{0}
}

func (m *String) XXX_Unmarshal(b []byte) error {
 return xxx_messageInfo_String.Unmarshal(m, b)
}
func (m *String) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 return xxx_messageInfo_String.Marshal(b, m, deterministic)
}
func (m *String) XXX_Merge(src proto.Message) {
 xxx_messageInfo_String.Merge(m, src)
}
func (m *String) XXX_Size() int {
 return xxx_messageInfo_String.Size(m)
}
func (m *String) XXX_DiscardUnknown() {
 xxx_messageInfo_String.DiscardUnknown(m)
}

var xxx_messageInfo_String proto.InternalMessageInfo

func (m *String) GetValue() *String {
 if m != nil {
  return m.Value
 }
 return nil
}

func init() {
 proto.RegisterType((*String)(nil), "main.String")
}

func init() { proto.RegisterFile("hello.proto", fileDescriptor_61ef911816e0a8ce) }

var fileDescriptor_61ef911816e0a8ce = []byte{
 // 84 bytes of a gzipped FileDescriptorProto
 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9,
 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x53, 0xd2, 0xe1,
 0x62, 0x0b, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x17, 0x52, 0xe2, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d,
 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0xe2, 0xd1, 0x03, 0xc9, 0xeb, 0x41, 0x24, 0x83, 0x20,
 0x52, 0x49, 0x6c, 0x60, 0xad, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0x0c, 0x0e, 0x54,
 0x49, 0x00, 0x00, 0x00,
}
  • String 类型自动生成了一组方法,其中 ProtoMessage 方法表示这是一个实现了 proto.Message 接口的方法。此外 Protobuf 还为每个成员生成了一个Get方法,能够提供便捷的取值方式,并且处理了一些空指针取值的情况,还能够通过 Reset 方法来重置该参数。
  • .pb.go 文件的初始化方法,注意 fileDescriptor 的相关语句。fileDescriptor_61ef911816e0a8ce 表示是一个经过编译后的 proto 文件,是对 proto 文件的整体描述,其包含了 proto 文件名、引用(import)内容、包(package)名、选项设置、所有定义的消息体(message)、所有定义的枚举(enum)、所有定义的服务( service)、所有定义的方法(rpc method)等等内容。
  • 每一个 Message Type 中都包含了 Descriptor 方法,Descriptor 代指对一个消息体(message)定义的描述,而这一个方法则会在 fileDescriptor 中寻找属于自己 Message Field 所在的位置再进行返回。

Protobuf 和 RPC组合

基于 String 类型,重新实现 HelloService 服务

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
 "log"
 "net"
 "net/rpc"
 "rpc/protoc"
)

// HelloService is rpc server obj
type HelloService struct{}

//Hello方法的输入参数和输出的参数均改用 Protobuf 定义的 String 类型表示。
//因为新的输入参数为结构体类型,因此改用指针类型作为输入参数,函数的内部代码同时也做了相应的调整。
func (p *HelloService) Hello(request *protoc.String, reply *protoc.String) error {
 reply.Value = "hello:" + request.GetValue()
 return nil
}

func main() {
 rpc.RegisterName("HelloService", new(HelloService))

 listener, err := net.Listen("tcp", ":1234")
 if err != nil {
  log.Fatal("ListenTCP error:", err)
 }

 conn, err := listener.Accept()
 if err != nil {
  log.Fatal("Accept error", err)
 }

 rpc.ServeConn(conn)
}

下面是客户端请求HelloService服务的代码 client.go:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
 "fmt"
 "log"
 "net/rpc"
 "rpc/protoc"
)

func main() {
 client, err := rpc.Dial("tcp", "localhost:1234")
 if err != nil {
  log.Fatal("dialing err:", err)
 }

 var reply = &protoc.String{}
 var param = &protoc.String{
  Value: "hello wekenw",
 }

 err = client.Call("HelloService.Hello", &param, &reply)
 if err != nil {
  log.Fatal(err)
 }
 fmt.Println(reply)
}

开启服务器端,开启客户端。客户端的执行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ go run client.go
value:"hello:hello wekenw"

图片及部分相关技术知识点来源于网络搜索,侵权删!

参考资料:

https://blog.csdn.net/qq_22660775/article/details/89163881

https://docs.microsoft.com/zh-cn/dotnet/architecture/grpc-for-wcf-developers/protobuf-reserved

https://golang2.eddycjy.com/posts/ch3/01-simple-grpc-protobuf/

《Go语言高级编程》

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微客鸟窝 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
理解Golang组件protobuf
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
平也
2020/04/03
7270
Go是如何实现protobuf的编解码的(1): 原理
各位朋友咱们又见面了,我是大彬,今天聊一聊Go是如何实现protobuf编解码的。
大彬
2019/09/09
1.1K0
Go是如何实现protobuf的编解码的(1): 原理
Go微服务(二)——Protobuf详细入门
Protobuf是Protocol Buffers的简称,它是Google公司开发的⼀种数据描述语⾔,并于2008年对外开 源。Protobuf刚开源时的定位类似于XML、JSON等数据描述语⾔,通过附带⼯具⽣成代码并实现将结 构化数据序列化的功能。但是我们更关注的是Protobuf作为接⼝规范的描述语⾔,可以作为设计安全的 跨语⾔PRC接⼝的基础⼯具。
传说之下的花儿
2023/04/16
3.7K0
Go微服务(二)——Protobuf详细入门
ProtoBuf插件原理 - 未完
这里以2.6.1为例,查看protobuf-2.6.1/src/google/protobuf/compiler/main.cc代码,默认注册cpp\java\python 3个generator。最后调用cli.Run接口执行。
王亚昌
2020/05/06
1.6K0
golang源码分析:protoc-gen-validate
业务代码中有很多参数校验的代码,如果手动实现,会非常繁琐,https://github.com/go-playground/validator是一个非常不错的选择echo 源码分析(validator),但是对于grpc来说,在定义proto的时候使用直接定义参数的限制规则是一种更合理、更优雅的方式,插件https://github.com/bufbuild/protoc-gen-validate就是来帮助我们实现这一功能的。kratos框架也用到了这个插件。下面我们详细介绍下如何安装和使用。
golangLeetcode
2023/03/14
2.2K0
golang源码分析:protoc-gen-validate
go-protobuf, go-grpc-gateway和代码生成
代码生成是一种常用的生产效率技术。广义上看,编译器通过高级语言生产出低级语言或者机器码,也可以理解为一种代码生成。这种技术在现代的工程实践里往往比较常见:IDE通常自带了一些常见的单元测试生成工具;根据特定的snippet可以生成比较常用的代码片段;在go语言中,由于目前缺乏对范型对支持,为了节约重复代码,通常实现了类似技术也是使用代码生成。
王磊-字节跳动
2019/07/28
3.2K0
深入解析fabric的peer命令(终篇)fabric-protos-go
最后一句endorser.ProcessProposal(context.Background(),signedProposal)在fabric-protos-go包里都做了什么呢?
酒楼
2023/06/04
2820
深入解析fabric的peer命令(终篇)fabric-protos-go
protobuf的使用
先上官方使用文档 https://developers.google.cn/protocol-buffers/docs/proto3
码缘
2021/05/07
2.1K0
Go语言,gRPC 的使用了解--上
$ go get -u google.golang.org/grpc@v1.29.1
微客鸟窝
2021/11/19
9230
Go语言,gRPC 的使用了解--上
golang 源码分析:json格式请求grpc服务的
gRPC payload 的默认格式是 Protobuf,但是 gRPC-Go 的实现中也对外暴露了 Codec interface ,它支持任意的 payload 编码。我们可以使用任何一种格式,包括你自己定义的二进制格式、flatbuffers、或者JSON 格式。
golangLeetcode
2022/12/17
9500
签约掘金:一文带你玩转ProtoBuf 【文末抽奖】
在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML,在微服务架构中通常使用另外一个数据交换的协议的工具ProtoBuf。
王中阳Go
2022/10/26
9110
签约掘金:一文带你玩转ProtoBuf 【文末抽奖】
Go是如何实现protobuf的编解码的(2): 源码
上一篇文章Go是如何实现protobuf的编解码的(1):原理 中已经指出了Go语言数据和Protobuf数据的编解码是由包github.com/golang/protobuf/proto完成的,本编就来分析一下proto包是如何实现编解码的。
大彬
2019/09/09
4K0
Go是如何实现protobuf的编解码的(2): 源码
嵌入式linux之go语言开发(七)protobuf的使用
之前写过一篇博文:《如果终端采用protobuf与采集前置通信,能带来哪些变革?https://blog.csdn.net/yyz_1987/article/details/81147454》,介绍了使用protobuf作为序列化通信格式的诸多好处。
杨永贞
2020/08/04
1.2K0
Go - 如何编写 ProtoBuf 插件(二)?
上篇文章《Go - 如何编写 ProtoBuf 插件 (一) 》,分享了使用 proto3 的 自定义选项 可以实现插件的编写,说到基于 MethodOptions 和 ServiceOptions 选项去实现 method 和 service 自定义设置拦截器。
新亮
2021/12/20
6310
GRPC接口测试全通攻略
RPC的全称叫做Remote Procedure Call(远程过程调用),意思是将远程(非本地)的一个方法,当作本地的一个方法来调用的一种规范。举例来帮助大家理解:
TestOps
2022/04/08
3.1K0
GRPC接口测试全通攻略
Go gRPC Hello World
开始 gRPC 了,这篇文章学习使用 gRPC,输出一个 Hello World。
新亮
2019/08/01
4910
go grpc 初步笔记
像许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。
solate
2019/07/22
6460
Go每日一库之94:protobuf
protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。protobuf 是以二进制方式存储的,占用空间小,但也带来了可读性差的缺点。protobuf 在通信协议和数据存储等领域应用广泛。例如著名的分布式缓存工具 Memcached 的 Go 语言版本groupcache 就使用了 protobuf 作为其 RPC 数据格式。
luckpunk
2023/09/30
7030
gRPC 使用 protobuf 构建微服务
以前使用 Laravel 做 web 项目时,是根据 MVC 去划分目录结构的,即 Controller 层处理业务逻辑,Model 层处理数据库的 CURD,View 层处理数据渲染与页面交互。以及 MVP、MVVM 都是将整个项目的代码是集中在一个代码库中,进行业务处理。这种单一聚合代码的方式在前期实现业务的速度很快,但在后期会暴露很多问题:
aoho求索
2019/05/17
2.2K0
gRPC 使用 protobuf 构建微服务
Go微服务(三)——gRPC详细入门
这里会联合protobuf语法以及protobuf如何去定义rpc服务,前面我们只生成了结构体,现在我们要让他为我们同时把接口生成,有了响应的接口,我们就再也不用去手写接口了。
传说之下的花儿
2023/04/16
4.2K0
Go微服务(三)——gRPC详细入门
相关推荐
理解Golang组件protobuf
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验