go protobuf

61 篇文章 0 订阅
15 篇文章 0 订阅

理论

XML

同类、JSON 也可以用来存储此类结构化数据,但是使用ProtoBuf表示的数据能更加高效,并且将数据压缩得更小。

原理

ProtoBuf 是通过ProtoBuf编译器将与编程语言无关的特有的 .proto 后缀的数据结构文件编译成各个编程语言(Java,C/C++,Python)专用的类文件,然后通过Google提供的各个编程语言的支持库lib即可调用API。(关于proto结构体怎么编写,可自行查阅文档)

和json

json是这些年慢慢兴起的轻量级数据交换格式。比起老大哥XML。因其更快的解析速度和更小的体积,可谓是用过的都说好。一般情况下json足够满足你的大多数需求,但是在计算机领域,没有最快,只有更快。
当你的传输数据大到一定程度的时候,json的速度也不能满足你需求的时候,你就需要更快的protobuf。
protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。(百度百科)。
因为其使用二进制存储,所以会比json更快。但是缺点也是显而易见,二进制存储易读性很差。
我曾遇到要解析40M json的需求。在PC端,使用litjson需要解析10秒钟。但是将相同的内容通过protobuf再导出成bytes。只要17M。缩小了2.5倍左右。但是读取速度只要0.8秒,还包括了数据解析后的处理。
0.1秒和0.008秒可能给人差别不大,但是10秒和0.8秒的差别真的是天壤地别。

编解码性能

上述栗子只是简单的采样,实际上据我的实验发现

  • 次数在1千以下,ProtoBuf 的编码与解码性能,都与JSON不相上下,甚至还有比JSON差的趋势。
  • 次数在2千以上,ProtoBuf的编码解码性能,都比JSON高出很多。
  • 次数在10万以上,ProtoBuf的编解码性能就很明显了,远远高出JSON的性能。

内存占用

ProtoBuf的内存34,而JSON到达106 ,ProtoBuf的内存占用只有JSON的1/3.

 

参考资料:

https://www.cnblogs.com/jkko123/p/7161843.html

https://blog.csdn.net/qq_32760475/article/details/90167724

https://studygolang.com/articles/18655

仅做个人笔记,浏览请看原博主原文

 

1、下载protobuf的编译器protoc

地址:https://github.com/google/protobuf/releases

window:

    下载: protoc-3.3.0-win32.zip

    解压,把bin目录下的protoc.exe复制到GOPATH/bin下,GOPATH/bin加入环境变量。

    当然也可放在其他目录,需加入环境变量,能让系统找到protoc.exe

linux:

    下载:protoc-3.3.0-linux-x86_64.zip 或 protoc-3.3.0-linux-x86_32.zip

    解压,把bin目录下的protoc复制到GOPATH/bin下,GOPATH/bin加入环境变量。

    如果喜欢编译安装的,也可下载源码自行安装,最后将可执行文件加入环境变量。

mac:

    下载protoc-3.12.3-osx-x86_64.zip,解压并把bin目录下的protoc复制到GOPATH/bin下,GOPATH/bin加入环境变量。当然也可放在其他目录,需加入环境变量,能让系统找到protoc。

 

2.1、获取protobuf的编译器插件protoc-gen-go(官方)

    go get  github.com/golang/protobuf/protoc-gen-go

    如果成功,会在GOPATH/bin下生成protoc-gen-go.exe文件

2.2、获取protobuf的编译器插件protoc-gen-go(快于官方)

    go get github.com/gogo/protobuf/protoc-gen-gofast

    注意:protoc执行时要用

protoc --gofast_out=. myproto.proto

 

3、创建一个test.proto文件

//指定版本
//注意proto3与proto2的写法有些不同
syntax = "proto3";
 
//包名,通过protoc生成时go文件时
package test;
 
//手机类型
//枚举类型第一个字段必须为0
enum PhoneType {
    HOME = 0;
    WORK = 1;
}
 
//手机
message Phone {
    PhoneType type = 1;
    string number = 2;
}
 
//人
message Person {
    //后面的数字表示标识号
    int32 id = 1;
    string name = 2;
    //repeated表示可重复
    //可以有多个手机
    repeated Phone phones = 3;
}
 
//联系簿
message ContactBook {
    repeated Person persons = 1;
}

 

4、运行protoc命令

详见

https://blog.csdn.net/chushoufengli/article/details/107002970

生成:

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: test.proto
 
package pro
 
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 PhoneType int32
 
const (
	PhoneType_HOME PhoneType = 0
	PhoneType_WORK PhoneType = 1
)
 
var PhoneType_name = map[int32]string{
	0: "HOME",
	1: "WORK",
}
 
var PhoneType_value = map[string]int32{
	"HOME": 0,
	"WORK": 1,
}
 
func (x PhoneType) String() string {
	return proto.EnumName(PhoneType_name, int32(x))
}
 
func (PhoneType) EnumDescriptor() ([]byte, []int) {
	return fileDescriptor_c161fcfdc0c3ff1e, []int{0}
}
 
type Phone struct {
	Type                 PhoneType `protobuf:"varint,1,opt,name=type,proto3,enum=pro.PhoneType" json:"type,omitempty"`
	Number               string    `protobuf:"bytes,2,opt,name=number,proto3" json:"number,omitempty"`
	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
	XXX_unrecognized     []byte    `json:"-"`
	XXX_sizecache        int32     `json:"-"`
}
 
func (m *Phone) Reset()         { *m = Phone{} }
func (m *Phone) String() string { return proto.CompactTextString(m) }
func (*Phone) ProtoMessage()    {}
func (*Phone) Descriptor() ([]byte, []int) {
	return fileDescriptor_c161fcfdc0c3ff1e, []int{0}
}
 
func (m *Phone) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Phone.Unmarshal(m, b)
}
func (m *Phone) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Phone.Marshal(b, m, deterministic)
}
func (m *Phone) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Phone.Merge(m, src)
}
func (m *Phone) XXX_Size() int {
	return xxx_messageInfo_Phone.Size(m)
}
func (m *Phone) XXX_DiscardUnknown() {
	xxx_messageInfo_Phone.DiscardUnknown(m)
}
 
var xxx_messageInfo_Phone proto.InternalMessageInfo
 
func (m *Phone) GetType() PhoneType {
	if m != nil {
		return m.Type
	}
	return PhoneType_HOME
}
 
func (m *Phone) GetNumber() string {
	if m != nil {
		return m.Number
	}
	return ""
}
 
type Person struct {
	Id                   int32    `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
	Name                 string   `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
	Phones               []*Phone `protobuf:"bytes,3,rep,name=phones,proto3" json:"phones,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}
 
func (m *Person) Reset()         { *m = Person{} }
func (m *Person) String() string { return proto.CompactTextString(m) }
func (*Person) ProtoMessage()    {}
func (*Person) Descriptor() ([]byte, []int) {
	return fileDescriptor_c161fcfdc0c3ff1e, []int{1}
}
 
func (m *Person) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Person.Unmarshal(m, b)
}
func (m *Person) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Person.Marshal(b, m, deterministic)
}
func (m *Person) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Person.Merge(m, src)
}
func (m *Person) XXX_Size() int {
	return xxx_messageInfo_Person.Size(m)
}
func (m *Person) XXX_DiscardUnknown() {
	xxx_messageInfo_Person.DiscardUnknown(m)
}
 
var xxx_messageInfo_Person proto.InternalMessageInfo
 
func (m *Person) GetId() int32 {
	if m != nil {
		return m.Id
	}
	return 0
}
 
func (m *Person) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}
 
func (m *Person) GetPhones() []*Phone {
	if m != nil {
		return m.Phones
	}
	return nil
}
 
type ContactBook struct {
	Persons              []*Person `protobuf:"bytes,1,rep,name=persons,proto3" json:"persons,omitempty"`
	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
	XXX_unrecognized     []byte    `json:"-"`
	XXX_sizecache        int32     `json:"-"`
}
 
func (m *ContactBook) Reset()         { *m = ContactBook{} }
func (m *ContactBook) String() string { return proto.CompactTextString(m) }
func (*ContactBook) ProtoMessage()    {}
func (*ContactBook) Descriptor() ([]byte, []int) {
	return fileDescriptor_c161fcfdc0c3ff1e, []int{2}
}
 
func (m *ContactBook) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_ContactBook.Unmarshal(m, b)
}
func (m *ContactBook) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_ContactBook.Marshal(b, m, deterministic)
}
func (m *ContactBook) XXX_Merge(src proto.Message) {
	xxx_messageInfo_ContactBook.Merge(m, src)
}
func (m *ContactBook) XXX_Size() int {
	return xxx_messageInfo_ContactBook.Size(m)
}
func (m *ContactBook) XXX_DiscardUnknown() {
	xxx_messageInfo_ContactBook.DiscardUnknown(m)
}
 
var xxx_messageInfo_ContactBook proto.InternalMessageInfo
 
func (m *ContactBook) GetPersons() []*Person {
	if m != nil {
		return m.Persons
	}
	return nil
}
 
func init() {
	proto.RegisterEnum("pro.PhoneType", PhoneType_name, PhoneType_value)
	proto.RegisterType((*Phone)(nil), "pro.Phone")
	proto.RegisterType((*Person)(nil), "pro.Person")
	proto.RegisterType((*ContactBook)(nil), "pro.ContactBook")
}
 
func init() { proto.RegisterFile("test.proto", fileDescriptor_c161fcfdc0c3ff1e) }
 
var fileDescriptor_c161fcfdc0c3ff1e = []byte{
	// 213 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x8f, 0xcf, 0x4a, 0xc4, 0x30,
	0x10, 0xc6, 0x4d, 0xdb, 0x8d, 0xee, 0x14, 0xca, 0x32, 0x07, 0xc9, 0xcd, 0x12, 0x10, 0x8a, 0x87,
	0x3d, 0xac, 0x3e, 0x81, 0x8b, 0x20, 0x88, 0x6c, 0x09, 0x82, 0xe7, 0xd6, 0x06, 0x2c, 0xd2, 0x4c,
	0x48, 0xe2, 0xa1, 0x6f, 0x2f, 0x1d, 0xab, 0x7b, 0xfb, 0x92, 0xdf, 0xf7, 0x27, 0x01, 0x48, 0x36,
	0xa6, 0xbd, 0x0f, 0x94, 0x08, 0x73, 0x1f, 0x48, 0x1f, 0x61, 0xd3, 0x7e, 0x92, 0xb3, 0xa8, 0xa1,
	0x48, 0xb3, 0xb7, 0x4a, 0xd4, 0xa2, 0xa9, 0x0e, 0xd5, 0xe2, 0xd9, 0x33, 0x79, 0x9b, 0xbd, 0x35,
	0xcc, 0xf0, 0x1a, 0xa4, 0xfb, 0x9e, 0x7a, 0x1b, 0x54, 0x56, 0x8b, 0x66, 0x6b, 0xd6, 0x93, 0x6e,
	0x41, 0xb6, 0x36, 0x44, 0x72, 0x58, 0x41, 0x36, 0x0e, 0xdc, 0xb1, 0x31, 0xd9, 0x38, 0x20, 0x42,
	0xe1, 0xba, 0xc9, 0xae, 0x7e, 0xd6, 0xa8, 0x41, 0xfa, 0xa5, 0x38, 0xaa, 0xbc, 0xce, 0x9b, 0xf2,
	0x00, 0xe7, 0x2d, 0xb3, 0x12, 0xfd, 0x00, 0xe5, 0x91, 0x5c, 0xea, 0x3e, 0xd2, 0x23, 0xd1, 0x17,
	0xde, 0xc2, 0xa5, 0xe7, 0x81, 0xa8, 0x04, 0x67, 0xca, 0xdf, 0x0c, 0xdf, 0x99, 0x3f, 0x76, 0x77,
	0x03, 0xdb, 0xff, 0x27, 0xe3, 0x15, 0x14, 0xcf, 0xa7, 0xd7, 0xa7, 0xdd, 0xc5, 0xa2, 0xde, 0x4f,
	0xe6, 0x65, 0x27, 0x7a, 0xc9, 0x3f, 0xbf, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x25, 0x00, 0x9c,
	0x6d, 0x07, 0x01, 0x00, 0x00,
}

 

5、在go语言中使用protobuf

package main;
 
import (
    "github.com/golang/protobuf/proto"
    "protobuf/test"
    "io/ioutil"
    "os"
    "fmt"
)
 
func write() {
    p1 := &test.Person{
        Id:   1,
        Name: "小张",
        Phones: []*test.Phone{
            {test.PhoneType_HOME, "111111111"},
            {test.PhoneType_WORK, "222222222"},
        },
    };
    p2 := &test.Person{
        Id:   2,
        Name: "小王",
        Phones: []*test.Phone{
            {test.PhoneType_HOME, "333333333"},
            {test.PhoneType_WORK, "444444444"},
        },
    };
 
    //创建地址簿
    book := &test.ContactBook{};
    book.Persons = append(book.Persons, p1);
    book.Persons = append(book.Persons, p2);
 
    //编码数据
    data, _ := proto.Marshal(book);
    //把数据写入文件
    ioutil.WriteFile("./test.txt", data, os.ModePerm);
}
 
func read() {
    //读取文件数据
    data, _ := ioutil.ReadFile("./test.txt");
    book := &test.ContactBook{};
    //解码数据
    proto.Unmarshal(data, book);
    for _, v := range book.Persons {
        fmt.Println(v.Id, v.Name);
        for _, vv := range v.Phones {
            fmt.Println(vv.Type, vv.Number);
        }
    }
}
 
func main() {
    write();
    read();
}

 

6、运行

1 张三

HOME 123

WORK 321

 

7、联调

javascript前端如何使用google-protobuf

https://blog.csdn.net/arvin_kai/article/details/77532595

https://www.baidu.com/s?ie=UTF-8&wd=%E5%89%8D%E7%AB%AF%E8%A7%A3%E6%9E%90%20protobuf

即:test.proto文件是前后端都要的,前后端的test.proto是一样的定义即同一个文件,前端使用protoc命令转换为js文件,我们后端转为go文件,这也就是protobuf所定义的语言无关性,同json

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值