介绍
protobuf
(protocol buffer) 是谷歌内部的混合语言数据标准。
protocol buffers 是一种与语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML(应该只是传输数据的功能和数据结构化的类比)。
序列化:是将对象的状态信息转换为可以存储或传输的形式的过程,序列化后的数据要确保能通过反序列化后还原出来
优点:
- 语言无关,平台无关
Protobuf支持Java, C++, Python等多种语言,支持多个平台。 - 高效
比XML更小(3~10倍),更快(20 ~ 100倍),更为简单。 - 扩展性,兼容性好
你可以更新数据结构,而不影响和破坏原有的旧程序。
安装
二进制可执行文件(命令)
安装地址:
https://github.com/protocolbuffers/protobuf/releases
下载自己系统需要的压缩包
就比如我的系统时win11,64位的,下载的就是这个
然后将解压后文件的/bin目录添加到环境变量中
打开CMD或是powershell,查看版本号protoc --version
如果有输出就说明添加成功了。
安装解析工具
如果是在go语言中使用,还需要下载一个解析.proto文件的工具
go get github.com/golang/protobuf/protoc-gen-go
它会将.proto文件转为.pb.go文件。
安装插件
如果想让Goland识别出.proto文件还需要安装一个插件
File–>Settings–>Plugins
使用
新建一个项目,在项目下新建一个文件夹用来存放.proto文件
user.proto文件
// 指定当前版本
syntax = "proto3";
// option go_package = "path;name";
// path表示生成的go文件存放地址,会自动生成目录
// name 表示生成go文件所属包名
option go_package = "../service";
// 指定文件生成出来的package
package service;
message User {
string username = 1;
int32 age = 2;
optional string password = 3;
repeated bool sex = 4;
}
在pbfile目录下执行
protoc --go_out=./ user.proto
就会发现生成了一个
user.pb.go文件(这里的都是默认生成的,不要改,生成的时需要导入依赖)
// 指定当前版本
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.21.1
// source: user.proto
// 指定文件生成出来的package
package service
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
Password *string `protobuf:"bytes,3,opt,name=password,proto3,oneof" json:"password,omitempty"`
Sex []bool `protobuf:"varint,4,rep,packed,name=sex,proto3" json:"sex,omitempty"`
}
func (x *User) Reset() {
*x = User{}
if protoimpl.UnsafeEnabled {
mi := &file_user_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *User) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*User) ProtoMessage() {}
func (x *User) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use User.ProtoReflect.Descriptor instead.
func (*User) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{0}
}
func (x *User) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *User) GetAge() int32 {
if x != nil {
return x.Age
}
return 0
}
func (x *User) GetPassword() string {
if x != nil && x.Password != nil {
return *x.Password
}
return ""
}
func (x *User) GetSex() []bool {
if x != nil {
return x.Sex
}
return nil
}
var File_user_proto protoreflect.FileDescriptor
var file_user_proto_rawDesc = []byte{
0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x74, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a,
0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x08, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03,
0x73, 0x65, 0x78, 0x18, 0x04, 0x20, 0x03, 0x28, 0x08, 0x52, 0x03, 0x73, 0x65, 0x78, 0x42, 0x0b,
0x0a, 0x09, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x0c, 0x5a, 0x0a, 0x2e,
0x2e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_user_proto_rawDescOnce sync.Once
file_user_proto_rawDescData = file_user_proto_rawDesc
)
func file_user_proto_rawDescGZIP() []byte {
file_user_proto_rawDescOnce.Do(func() {
file_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_user_proto_rawDescData)
})
return file_user_proto_rawDescData
}
var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_user_proto_goTypes = []interface{}{
(*User)(nil), // 0: service.User
}
var file_user_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_user_proto_init() }
func file_user_proto_init() {
if File_user_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*User); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_user_proto_msgTypes[0].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_user_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_user_proto_goTypes,
DependencyIndexes: file_user_proto_depIdxs,
MessageInfos: file_user_proto_msgTypes,
}.Build()
File_user_proto = out.File
file_user_proto_rawDesc = nil
file_user_proto_goTypes = nil
file_user_proto_depIdxs = nil
}
新建一个main.go文件
package main
import (
"fmt"
"google.golang.org/protobuf/proto"
"protobuf/helloWorld/service"
)
func main() {
var a = "a"
user := &service.User{
Username: "张三",
Age: 12,
Sex: []bool{true,true,false},
Password: &a,
}
// 序列化
marshal, err := proto.Marshal(user)
if err != nil {
panic(err)
}
fmt.Printf("序列化后:%v\n",marshal)
// 反序列化
var other = &service.User{}
err = proto.Unmarshal(marshal, other)
if err != nil {
panic(err)
}
fmt.Printf("反序列化:%v",user)
}
proto文件字段解释
通常文件名建议命名格式为 包名.消息名.proto
syntax = "proto3";
// 指定使用的proto版本
// option go_package = "path;name";
// path表示生成的go文件存放地址,会自动生成目录
// name 表示生成go文件所属包名
option go_package = "../service";
// 指定文件生成出来的package
package service;
包声明
proto
文件以package
声明开头,这有助于防止不同项目之间命名冲突。
字段规则
string username = 1;
int32 age = 2;
optional string password = 3;
repeated bool sex = 4;
required
:消息体中必填字段,在proto3中默认为该字段。optional
: 消息体中可选字段。repeated
: 消息体中可重复字段,重复的值的顺序会被保留。其中,proto3默认使用packed方式存储,这样编码方式比较节省内存。
标识号
string username = 1;
int32 age = 2;
optional string password = 3;
repeated bool sex = 4;
在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[0,2^29-1]范围内的一个整数。以上为例username的标识号为1,age标识号为2…
默认值
- 对于strings,默认是一个空string
- 对于bytes,默认是一个空bytes
- 对于bools,默认false
- 对于数值类型,默认0
- 对于枚举类型,默认是第一个定义的枚举值,必须为0;
- 对于消息类型(message),域没有被设置,确切的消息是根据具体语言定义
嵌套
当用于传输和存储较为复杂的信息时,可以将message嵌套写来完成,就比如
message Parents{
message Child{
string name = 1;
int64 age = 2;
}
}