微服务系列(2)Protobuf介绍
Protobuf是Google旗下的一款与平台无关,语言无关,可扩展的序列化结构数据格式。所以很适合做数据存储和作为不同应用,不同语言之间互相通信的数据交换格式,只要实现相同的协议格式即同一proto文件被编译成不同的语言版本,加入到各自的工程中去。这样不同语言就可以解析其他语言通过protobuf序列化的数据。目前官网提供了C++,Python,Java,Go等语言的支持。Google在2008年7月7日将其作为开源项目对外公布。
protobuf简介
Google Protocol Buffer(简称Protobuf)是一种轻便高效的结构化数据存储格式,平台无关,语言无关、可扩展,可用于通讯协议和数据存储等领域。
数据交互的格式比较
数据交互xml、json、protobuf格式比较
JSON:一般的web项目中,最流行的主要还是json。因为浏览器对于json数据支持非常好,有很多内建的函数支持。
XML:在webservice中应用最为广泛,但相比于json,它的数据更加冗余,因为需要成对的闭合标签。json使用了键值对的方式,不仅压缩了一定的数据空间,同时也具有可读性。
Protobuf:后起之秀,是Google开源的一种数据格式,适合高性能,对响应速度有要求的数据传输场景。因为protobuf是二进制的数据格式,需要编码和解码。数据本身不具有可读性。因此只能反序列化之后得到真正可读的数据。
相对于其它,Protobuf更有优势
- 序列化后体积相比json和xml很小,适合网络传输
- 支持跨平台多语言
- 消息格式升级和兼容性还不错
- 序列化反序列化速度快,快于json的处理速度
Protobuf的优缺点
Protobuf的优点
Protobuf有如xml,不过它更小,更快,也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需使用Protobuf对数据结构进行一次描述,即可利用各种不同语言或从各种不同的数据流中对你的结构化数据进行轻松读写。它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。Protobuf语义更清晰,无需类似xml解析器的东西。使用Protobuf无需学习复杂的文档对象模型,Protobuf的编程模式比较友好,简单易学,同时它拥有良好的文档和示例,对于喜欢简单事物的人们而言,Protobuf比其他的技术更加有吸引力。
Protobuf的不足
Protobuf与xml相比也有不足之处。它功能简单,无法用来表示复杂的概念。xml已经成为多种行业标准的编写工具,Protobuf只是Google公司内部使用的工具,在通用性上还差很多。由于文本并不适合用来描述数据结构,所以Protobuf也不适合用来对基于文本的标记文档建模。另外,由于xml具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上Protobuf不行,它以二进制的方式存储,除非你有.proto定义,否则你无法直接读出Protobuf的任何内容。
Protobuf安装
安装protoc
可以上https://github.com/google/protobuf/releases下载对应平台的二进制版本,并将protoc放到PATH环境变量中。
安装完执行protoc --version
验证安装
1protoc --version
2libprotoc 3.11.4
安装proto包
1go get -u github.com/golang/protobuf/proto
安装protoc-gen-go插件
1go get -u github.com/golang/protobuf/protoc-gen-go
执行完会在$GOPATH/bin
目录下生成二进制protoc-gen-go
文件
Protobuf基本语法
要想使用protobuf必须先定义proto文件。所以需要先数据Protobuf的消息定义的相关语法。
定义一个消息类型
1syntax = "proto3";
2
3// 请求
4message PandaRequest {
5 // 姓名
6 string name = 1;
7 // 身高
8 int32 height = 2;
9 // 体重
10 repeated int32 weight =3;
11}
12
13// 响应
14message PandaResponse{
15 // 错误号
16 int32 err = 1;
17 // 错误信息
18 string errmessage = 2;
19}
文件的第一行指定了你正在使用proto3的语法;如果没有指定,编译器默认使用proto2,指定语法必须是文件的非空非注释的首行。上面的例子中,所有字段都是标量类型,两个整型一个string类型。repeated
关键字表示重复的在go语言中用切片来代表。每个字段都有唯一的标识符。
当用protobuf编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取,设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。
protobuf类型
.proto Type | Notes | Go Type |
---|---|---|
double | float64 | |
float | float32 | |
int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 |
int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 |
uint32 | Uses variable-length encoding. | uint32 |
uint64 | Uses variable-length encoding. | uint64 |
sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 |
sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 |
fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. | uint32 |
fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. | uint64 |
sfixed32 | Always four bytes. | int32 |
sfixed64 | Always eight bytes. | int64 |
bool | bool | |
string | A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232. | string |
bytes | May contain any arbitrary sequence of bytes no longer than 232. | []byte |
定义服务
如果想要将消息类型用在rpc系统中,可以在.proto文件中定义一个rpc服务接口,protobuf编译器将会根据所选择的语言生成服务结构代码以及存根。如想要定义一个rpc服务并具有一个方法,该方法能够接收SearchRequest并返回一个SearchResponse,此时可以在.proto中进行如下定义:
1service SearchService {
2 rpc Search (SearchRequest) returns (SearchResponse);
3}
生成访问类
1protoc --go_out ./ path/to/file.proto
执行完会生成一个.pb.go的文件,上例中的代码生成的go文件内容如下:
1// Code generated by protoc-gen-go. DO NOT EDIT.
2// source: proto/panda.proto
3
4package panda
5
6import (
7 fmt "fmt"
8 proto "github.com/golang/protobuf/proto"
9 math "math"
10)
11
12// Reference imports to suppress errors if they are not otherwise used.
13var _ = proto.Marshal
14var _ = fmt.Errorf
15var _ = math.Inf
16
17// This is a compile-time assertion to ensure that this generated file
18// is compatible with the proto package it is being compiled against.
19// A compilation error at this line likely means your copy of the
20// proto package needs to be updated.
21const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22
23// 请求
24type PandaRequest struct {
25 // 姓名
26 Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
27 // 身高
28 Height int32 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
29 // 体重
30 Weight []int32 `protobuf:"varint,3,rep,packed,name=weight,proto3" json:"weight,omitempty"`
31 XXX_NoUnkeyedLiteral struct{} `json:"-"`
32 XXX_unrecognized []byte `json:"-"`
33 XXX_sizecache int32 `json:"-"`
34}
35
36func (m *PandaRequest) Reset() { *m = PandaRequest{} }
37func (m *PandaRequest) String() string { return proto.CompactTextString(m) }
38func (*PandaRequest) ProtoMessage() {}
39func (*PandaRequest) Descriptor() ([]byte, []int) {
40 return fileDescriptor_3314ebc5778d4431, []int{0}
41}
42
43func (m *PandaRequest) XXX_Unmarshal(b []byte) error {
44 return xxx_messageInfo_PandaRequest.Unmarshal(m, b)
45}
46func (m *PandaRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
47 return xxx_messageInfo_PandaRequest.Marshal(b, m, deterministic)
48}
49func (m *PandaRequest) XXX_Merge(src proto.Message) {
50 xxx_messageInfo_PandaRequest.Merge(m, src)
51}
52func (m *PandaRequest) XXX_Size() int {
53 return xxx_messageInfo_PandaRequest.Size(m)
54}
55func (m *PandaRequest) XXX_DiscardUnknown() {
56 xxx_messageInfo_PandaRequest.DiscardUnknown(m)
57}
58
59var xxx_messageInfo_PandaRequest proto.InternalMessageInfo
60
61func (m *PandaRequest) GetName() string {
62 if m != nil {
63 return m.Name
64 }
65 return ""
66}
67
68func (m *PandaRequest) GetHeight() int32 {
69 if m != nil {
70 return m.Height
71 }
72 return 0
73}
74
75func (m *PandaRequest) GetWeight() []int32 {
76 if m != nil {
77 return m.Weight
78 }
79 return nil
80}
81
82// 响应
83type PandaResponse struct {
84 // 错误号
85 Err int32 `protobuf:"varint,1,opt,name=err,proto3" json:"err,omitempty"`
86 // 错误信息
87 Errmessage string `protobuf:"bytes,2,opt,name=errmessage,proto3" json:"errmessage,omitempty"`
88 XXX_NoUnkeyedLiteral struct{} `json:"-"`
89 XXX_unrecognized []byte `json:"-"`
90 XXX_sizecache int32 `json:"-"`
91}
92
93func (m *PandaResponse) Reset() { *m = PandaResponse{} }
94func (m *PandaResponse) String() string { return proto.CompactTextString(m) }
95func (*PandaResponse) ProtoMessage() {}
96func (*PandaResponse) Descriptor() ([]byte, []int) {
97 return fileDescriptor_3314ebc5778d4431, []int{1}
98}
99
100func (m *PandaResponse) XXX_Unmarshal(b []byte) error {
101 return xxx_messageInfo_PandaResponse.Unmarshal(m, b)
102}
103func (m *PandaResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
104 return xxx_messageInfo_PandaResponse.Marshal(b, m, deterministic)
105}
106func (m *PandaResponse) XXX_Merge(src proto.Message) {
107 xxx_messageInfo_PandaResponse.Merge(m, src)
108}
109func (m *PandaResponse) XXX_Size() int {
110 return xxx_messageInfo_PandaResponse.Size(m)
111}
112func (m *PandaResponse) XXX_DiscardUnknown() {
113 xxx_messageInfo_PandaResponse.DiscardUnknown(m)
114}
115
116var xxx_messageInfo_PandaResponse proto.InternalMessageInfo
117
118func (m *PandaResponse) GetErr() int32 {
119 if m != nil {
120 return m.Err
121 }
122 return 0
123}
124
125func (m *PandaResponse) GetErrmessage() string {
126 if m != nil {
127 return m.Errmessage
128 }
129 return ""
130}
131
132func init() {
133 proto.RegisterType((*PandaRequest)(nil), "PandaRequest")
134 proto.RegisterType((*PandaResponse)(nil), "PandaResponse")
135}
136
137func init() {
138 proto.RegisterFile("proto/panda.proto", fileDescriptor_3314ebc5778d4431)
139}
140
141var fileDescriptor_3314ebc5778d4431 = []byte{
142 // 151 bytes of a gzipped FileDescriptorProto
143 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0x28, 0xca, 0x2f,
144 0xc9, 0xd7, 0x2f, 0x48, 0xcc, 0x4b, 0x49, 0xd4, 0x03, 0xb3, 0x95, 0x82, 0xb8, 0x78, 0x02, 0x40,
145 0xdc, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x21, 0x2e, 0x96, 0xbc, 0xc4, 0xdc, 0x54,
146 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x30, 0x5b, 0x48, 0x8c, 0x8b, 0x2d, 0x23, 0x35, 0x33,
147 0x3d, 0xa3, 0x44, 0x82, 0x49, 0x81, 0x51, 0x83, 0x35, 0x08, 0xca, 0x03, 0x89, 0x97, 0x43, 0xc4,
148 0x99, 0x15, 0x98, 0x41, 0xe2, 0x10, 0x9e, 0x92, 0x23, 0x17, 0x2f, 0xd4, 0xcc, 0xe2, 0x82, 0xfc,
149 0xbc, 0xe2, 0x54, 0x21, 0x01, 0x2e, 0xe6, 0xd4, 0xa2, 0x22, 0xb0, 0x99, 0xac, 0x41, 0x20, 0xa6,
150 0x90, 0x1c, 0x17, 0x57, 0x6a, 0x51, 0x51, 0x6e, 0x6a, 0x71, 0x71, 0x62, 0x7a, 0x2a, 0xd8, 0x58,
151 0xce, 0x20, 0x24, 0x91, 0x24, 0x36, 0xb0, 0xeb, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x84,
152 0x8c, 0xf0, 0x38, 0xb2, 0x00, 0x00, 0x00,
153}
参考
- 原文作者:黄忠德
- 原文链接:https://huangzhongde.cn/post/2020-03-14-Microservice_Protobuf/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。