首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go是如何实现protobuf的编解码的(1): 原理

Go是如何实现protobuf的编解码的(1): 原理

作者头像
大彬
发布于 2019-09-09 10:02:46
发布于 2019-09-09 10:02:46
1.2K00
代码可运行
举报
文章被收录于专栏:一起学Golang一起学Golang
运行总次数:0
代码可运行

这是Go语言充电站的第 30 期分享。

各位朋友咱们又见面了,我是大彬,今天聊一聊Go是如何实现protobuf编解码的。

这是一篇姊妹篇文章,本编是第一篇:

1.Go是如何实现protobuf的编解码的(1): 原理2.Go是如何实现protobuf的编解码的(2): 源码

Protocol Buffers介绍

Protocol buffers缩写为protobuf,是由Google创造的一种用于序列化的标记语言,项目Github仓库:https://github.com/protocolbuffers/protobuf。

Protobuf主要用于不同的编程语言的协作RPC场景下,定义需要序列化的数据格式。Protobuf本质上仅仅是一种用于交互的结构式定义,从功能上XMLJSON等各种其他的交互形式都并无本质不同,只负责定义不负责数据编解码

其官方介绍如下:

Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

Protocol buffers的多语言支持

protobuf是支持多种编程语言的,即多种编程语言的类型数据可以转换成protobuf定义的类型数据,各种语言的类型对应可以看此介绍:https://developers.google.com/protocol-buffers/docs/proto3#scalar 。

我们介绍一下protobuf对多语言的支持原理。protobuf有个程序叫protoc,它是一个编译程序,负责把proto文件编译成对应语言的文件,它已经支持了C++、C#、JavaPython,而对于Go和Dart需要安装插件才能配合生成对于语言的文件。

对于C++,protoc可以把a.proto,编译成a.pb.ha.pb.cc

对于Go,protoc需要使用插件protoc-gen-go,把a.proto,编译成a.pb.go,其中包含了定义的数据类型,它的序列化和反序列化函数等。

敲黑板,对Go语言,protoc只负责利用protoc-gen-go把proto文件编译成Go语言文件,并不负责序列化和反序列化,生成的Go语言文件中的序列化和反序列化操作都是只是wrapper。

那Go语言对protobuf的序列化和反序列化,是由谁完成的?

github.com/golang/protobuf/proto完成,它负责把结构体等序列化成proto数据([]byte),把proto数据反序列化成Go结构体。

OK,原理部分就铺垫这些,看一个简单样例,了解protoc和protoc-gen-go的使用,以及进行序列化和反序列化操作。

一个Hello World样例

根据上面的介绍,Go语言使用protobuf我们要先安装2个工具:protoc和protoc-gen-go。

安装protoc和protoc-gen-go

首先去下载页:https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.1 ,下载符合你系统的protoc,本文示例版本如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  protoc-3.9.0-osx-x86_64 tree .
.
├── bin
│   └── protoc
├── include
│   └── google
│       └── protobuf
│           ├── any.proto
│           ├── api.proto
│           ├── compiler
│           │   └── plugin.proto
│           ├── descriptor.proto
│           ├── duration.proto
│           ├── empty.proto
│           ├── field_mask.proto
│           ├── source_context.proto
│           ├── struct.proto
│           ├── timestamp.proto
│           ├── type.proto
│           └── wrappers.proto
└── readme.txt

5 directories, 14 files

protoc的安装步骤在readme.txt中:

To install, simply place this binary somewhere in your PATH.

protoc-3.9.0-osx-x86_64/bin加入到PATH。

If you intend to use the included well known types then don't forget to copy the contents of the 'include' directory somewhere as well, for example into '/usr/local/include/'.

如果使用已经定义好的类型,即上面include目录*.proto文件中的类型,把include目录下文件,拷贝到/usr/local/include/

安装protoc-gen-go:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
go get –u github.com/golang/protobuf/protoc-gen-go

检查安装,应该能查到这2个程序的位置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  fabric git:(release-1.4) which protoc
/usr/local/bin/protoc
➜  fabric git:(release-1.4) which protoc-gen-go
/Users/shitaibin/go/bin/protoc-gen-go

Hello world

创建了一个使用protoc的小玩具,项目地址:https://github.com/Shitaibin/golang_step_by_step/tree/master/protobuf/helloworld1 。

它的目录结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  protobuf git:(master) tree helloworld1
helloworld1
├── main.go
├── request.proto
└── types
    └── request.pb.go

定义proto文件

使用proto3,定义一个Request,request.proto内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// file: request.proto
syntax = "proto3";
package helloworld;
option go_package="./types";

message Request {
    string data = 1;
}

•syntax:protobuf版本,现在是proto3•package:不完全等价于Go的package,最好另行设定go_package,指定根据protoc文件生成的go语言文件的package名称。•message:会编译成Go的struct

string data = 1:代表request的成员data是string类型,该成员的id是1,protoc给每个成员都定义一个编号,编解码的时候使用编号代替使用成员名称,压缩数据量。

编译proto文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ protoc --go_out=. ./request.proto

--go_out指明了要把./request.proto编译成Go语言文件,生成的是./types/request.pb.go,注意观察一下为Request结构体生产的2个方法XXX_UnmarshalXXX_Marshal,文件内容如下:

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

package types

import (
    fmt "fmt"
    math "math"

    proto "github.com/golang/protobuf/proto"
)

// 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 Request struct {
    Data                 string   `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
    // 以下是protobuf自动填充的字段,protobuf需要使用
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

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

// 反序列化函数
func (m *Request) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_Request.Unmarshal(m, b)
}
// 序列化函数
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
    xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
    return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
    xxx_messageInfo_Request.DiscardUnknown(m)
}

var xxx_messageInfo_Request proto.InternalMessageInfo

// 获取字段
func (m *Request) GetData() string {
    if m != nil {
        return m.Data
    }
    return ""
}

func init() {
    proto.RegisterType((*Request)(nil), "helloworld.Request")
}

func init() { proto.RegisterFile("request.proto", fileDescriptor_7f73548e33e655fe) }

var fileDescriptor_7f73548e33e655fe = []byte{
    // 91 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x4a, 0x2d, 0x2c,
    0x4d, 0x2d, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xca, 0x48, 0xcd, 0xc9, 0xc9,
    0x2f, 0xcf, 0x2f, 0xca, 0x49, 0x51, 0x92, 0xe5, 0x62, 0x0f, 0x82, 0x48, 0x0a, 0x09, 0x71, 0xb1,
    0xa4, 0x24, 0x96, 0x24, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x4e, 0x9c, 0x51,
    0xec, 0x7a, 0xfa, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0xcd, 0xc6, 0x80, 0x00, 0x00,
    0x00, 0xff, 0xff, 0x2e, 0x52, 0x69, 0xb5, 0x4d, 0x00, 0x00, 0x00,
}

编写Go语言程序

下面这段测试程序就是创建了一个请求,序列化又反序列化的过程。

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

import (
    "fmt"

    "./types"
    "github.com/golang/protobuf/proto"
)

func main() {
    req := &types.Request{Data: "Hello LIB"}

    // Marshal
    encoded, err := proto.Marshal(req)
    if err != nil {
        fmt.Printf("Encode to protobuf data error: %v", err)
    }

    // Unmarshal
    var unmarshaledReq types.Request
    err = proto.Unmarshal(encoded, &unmarshaledReq)
    if err != nil {
        fmt.Printf("Unmarshal to struct error: %v", err)
    }

    fmt.Printf("req: %v\n", req.String())
    fmt.Printf("unmarshaledReq: %v\n", unmarshaledReq.String())
}

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  helloworld1 git:(master) go run main.go
req: data:"Hello LIB"
unmarshaledReq: data:"Hello LIB"
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一起学Golang 微信公众号,前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go是如何实现protobuf的编解码的(2): 源码
上一篇文章Go是如何实现protobuf的编解码的(1):原理 中已经指出了Go语言数据和Protobuf数据的编解码是由包github.com/golang/protobuf/proto完成的,本编就来分析一下proto包是如何实现编解码的。
大彬
2019/09/09
4.2K0
Go是如何实现protobuf的编解码的(2): 源码
理解Golang组件protobuf
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
平也
2020/04/03
7570
Go语言,Protobuf 极速入门!
Protobuf 是 Protocol Buffers 的简称,是一种与语言、平台无关,可扩展的序列化结构化数据的数据描述语言,Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。
微客鸟窝
2021/11/12
9600
Go语言,Protobuf 极速入门!
Go 使用 protobuf
安装 protoc ,protoc 是用来执行根据 proto 文件生成 代码的工具。
晓晨
2022/05/07
1.5K0
Go 使用 protobuf
go 安装protobuf
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。
仙士可
2022/01/18
2.3K0
go 安装protobuf
如何在Go中使用Protobuf
Protobuf对于Golang通过插件进行支持,因些需要安装protoc的执行环境,下面我们来一步步看下,如何搭建一个编译环境。
王亚昌
2018/08/03
1.1K0
go-protobuf, go-grpc-gateway和代码生成
代码生成是一种常用的生产效率技术。广义上看,编译器通过高级语言生产出低级语言或者机器码,也可以理解为一种代码生成。这种技术在现代的工程实践里往往比较常见:IDE通常自带了一些常见的单元测试生成工具;根据特定的snippet可以生成比较常用的代码片段;在go语言中,由于目前缺乏对范型对支持,为了节约重复代码,通常实现了类似技术也是使用代码生成。
王磊-字节跳动
2019/07/28
3.4K0
嵌入式linux之go语言开发(七)protobuf的使用
之前写过一篇博文:《如果终端采用protobuf与采集前置通信,能带来哪些变革?https://blog.csdn.net/yyz_1987/article/details/81147454》,介绍了使用protobuf作为序列化通信格式的诸多好处。
杨永贞
2020/08/04
1.3K0
Golang 序列化之 ProtoBuf
ProtoBuf: 是一套完整的 IDL(接口描述语言),出自Google,基于 C++ 进行的实现,开发人员可以根据 ProtoBuf 的语言规范生成多种编程语言(Golang、Python、Java 等)的接口代码,本篇只讲述 Golang 的基础操作。据说 ProtoBuf 所生成的二进制文件在存储效率上比 XML 高 3~10 倍,并且处理性能高 1~2 个数量级,这也是选择 ProtoBuf 作为序列化方案的一个重要因素之一。 安装 1.安装 protoc :Protoc下载地址(https://
李海彬
2018/03/19
1.5K0
Golang 序列化之 ProtoBuf
Go 语言中使用 Protocol Buffers
当前流行微服务框架,gRPC和Protocol Buffers 是 Go 的微服务框架常用的框架。
张云飞Vir
2020/04/09
1.4K0
【附近的人】序列化之白送篇---msgpack大战protobuf
本文没有什么亮点不高端不涉及高性能高并发而且网上一搜一大把发誓文章没有蹭mp和pb热点最后的末尾有会挂一个微信的广告
老李秀
2019/11/12
5.8K0
5个步骤搞定protoc环境安装
胖sir:不知道你有没有这样的疑惑,工作中发现很多新接触GO的小伙伴安装protoc的环境需要折腾很久。
阿兵云原生
2023/02/16
6.1K0
Golang Gin 实战(十二)| ProtoBuf 使用和源码分析原理实现
ProtoBuf最近几年也算比较流行,它是一种语言无关,平台无关,并且可以扩展,并结构数据序列化的方法。相比JSON/XML这类文本格式的,ProtoBuf是二进制的,更快更高效。对于这么好的特性,Gin当然是支持的。
飞雪无情
2020/06/23
3.2K0
Protobuf - 更小、更快、更简单的交互式数据语言
Protocol buffers 是 Google 的一种语言中立、平台中立,可扩展,用于序列化结构化数据的交互式数据语言。相比 JSON、XML,它更小、更快、更简单。
frank.
2020/11/06
1.3K0
Go 中的 gRPC 入门详解
golang-grpc 包提供了 gRPC 相关的代码库,通过这个库我们可以创建 gRPC 服务或客户端,首先需要安装他。
痴者工良
2021/04/26
3.2K0
Go Protobuf(比xml小3-10倍, 快20-100倍)
protocol buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小、更快、更为简单。你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏根据旧数据结构编译而成并且已部署的程序。
iginkgo18
2021/04/13
2.2K0
Go 开发者必备:Protocol Buffers 入门指南
在现代软件开发中,系统之间的高效通信至关重要,尤其是在微服务架构和分布式系统中。为了高效地传输数据并保证跨语言的兼容性,Protocol Buffers(简称 Protobuf) 应运而生。Protobuf 是 Google 开发的一种轻量、高效的序列化数据格式。它被广泛应用于微服务、RPC 框架以及大数据处理等领域。
陈明勇
2025/01/22
3.3K8
Go 开发者必备:Protocol Buffers 入门指南
高效的序列化/反序列化数据方式 Protobuf
上篇文章中其实已经讲过了 encode 的过程,这篇文章以 golang 为例,从代码实现的层面讲讲序列化和反序列化的过程。
一缕殇流化隐半边冰霜
2018/08/30
3.8K0
高效的序列化/反序列化数据方式 Protobuf
Go每日一库之94:protobuf
protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。protobuf 是以二进制方式存储的,占用空间小,但也带来了可读性差的缺点。protobuf 在通信协议和数据存储等领域应用广泛。例如著名的分布式缓存工具 Memcached 的 Go 语言版本groupcache 就使用了 protobuf 作为其 RPC 数据格式。
luckpunk
2023/09/30
8630
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
相关推荐
Go是如何实现protobuf的编解码的(2): 源码
更多 >
LV.0
这个人很懒,什么都没有留下~
交个朋友
加入[后端] 腾讯云技术交流站
后端架构设计 高可用系统实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档