介绍
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; } }