Go --- protobuf的介绍和使用

protobuf是谷歌的结构化数据序列化库,支持多种语言和平台。本文详细介绍了protobuf的序列化概念、优点、安装步骤、在Go语言中的使用,以及如何处理.proto文件。通过实例展示了如何创建.proto文件、生成.pb.go文件并进行序列化和反序列化操作。此外,还讲解了.proto文件字段规则,如标识号、字段类型和默认值。
摘要由CSDN通过智能技术生成

介绍

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;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值