Introducción a Protobuf
La biblioteca de serialización se usa a menudo en transmisión de red, RPC, acceso a bases de datos y otros entornos. Su rendimiento afecta directamente el rendimiento de todo el producto, por lo que es necesario que aprendamos y dominemos algunas bibliotecas de serialización excelentes.
protobuf es un lenguaje de lenguaje de descripción de datos desarrollado por Google, que puede serializar datos estructurados y puede usarse para almacenamiento de datos, protocolo de comunicación, etc. La versión oficial es compatible con Go, C ++, Java, Python y la versión comunitaria admite más lenguajes.
En comparación con JSON y XML, tiene las siguientes ventajas:
- conciso
- Tamaño pequeño: una décima parte de json, una vigésima parte del formato xml, una décima parte de la serialización binaria
- Velocidad rápida: la velocidad de análisis es 20 ~ 100 veces más rápida que XML
- Usando el compilador de Protobuf, puede generar códigos de acceso a datos que son más fáciles de usar en la programación
- Mejor compatibilidad, un principio del diseño de Protobuf es poder soportar bien la compatibilidad hacia abajo o hacia arriba
- Después de que protobuf serializa los datos en binario, el espacio ocupado es bastante pequeño, básicamente solo se reserva la parte de datos, y xml y json tendrán la estructura del mensaje en los datos;
Configuración IDE
De forma predeterminada, IDE goland no admite el tipo de archivo de protocolo protobuf ".proto". Para escribir el código en el archivo proto de manera más rápida y eficiente, introduciremos un complemento para admitirlo. El complemento admite la palabra clave mensajes de error de sintaxis y resaltado.
1. Archivo-> Configuración-> Complementos-> Ingrese al soporte de protobuf-> instalar y reinicie el IDE
2. Archivo-> Configuración-> Editor-> Tipos de archivo, busque Protobuf, regístrese para admitir * .proto
Configuración del compilador
Protobuffer pertenece a un acuerdo multiplataforma y entre idiomas. Tiene su propio protocolo de compilación , que es el compilador de Protobuf para
descargar el último paquete comprimido. Después de la descompresión, coloque el programa protocol.exe en el directorio bin de gopath, preferiblemente el directorio bin de gopath Agregar a las variables de entorno del sistema Enlace de
descarga: https://github.com/protocolbuffers/protobuf/releases
Prueba:
终端输入`protoc --version` 显示版本即代表安装成功
或
执行 `protoc -h` 正常输出 相关指令 没有报任何error,为安装成功
Instale el archivo de la biblioteca protobuf:
go get github.com/golang/protobuf/proto
Instale el complemento goprotobuf (complemento del compilador de Protobuf para la versión go):
go get github.com/golang/protobuf/protoc-gen-go
Ejemplo
Estructura de directorios:
Nota:
El archivo de formato proto que queremos escribir se almacena en el directorio protos
syntax = "proto3";
// 新版的proto编译器都要求文件中标注编译后文件的存储位置
// 即生成的.pb.go文件会存放在../pb中,并和该文件名保持一致
// test.proto => test.pb.go
option go_package = "../pb";
message Person {
string Name = 1;
int32 Age = 2;
}
Generar .pb.go
Vaya al directorio protos para ejecutar
protoc --go_out=. *.proto
Los archivos generados se almacenan en el directorio pb
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: test.proto
package pb
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Person struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=Age,proto3" json:"Age,omitempty"`
}
func (x *Person) Reset() {
*x = Person{
}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Person) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Person) ProtoMessage() {
}
func (x *Person) ProtoReflect() protoreflect.Message {
mi := &file_test_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 Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{
0}
}
func (x *Person) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Person) GetAge() int32 {
if x != nil {
return x.Age
}
return 0
}
var File_test_proto protoreflect.FileDescriptor
var file_test_proto_rawDesc = []byte{
0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2e, 0x0a, 0x06,
0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x41, 0x67,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x41, 0x67, 0x65, 0x42, 0x07, 0x5a, 0x05,
0x2e, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_test_proto_rawDescOnce sync.Once
file_test_proto_rawDescData = file_test_proto_rawDesc
)
func file_test_proto_rawDescGZIP() []byte {
file_test_proto_rawDescOnce.Do(func() {
file_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData)
})
return file_test_proto_rawDescData
}
var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_test_proto_goTypes = []interface{
}{
(*Person)(nil), // 0: Person
}
var file_test_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_test_proto_init() }
func file_test_proto_init() {
if File_test_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_test_proto_msgTypes[0].Exporter = func(v interface{
}, i int) interface{
} {
switch v := v.(*Person); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{
}).PkgPath(),
RawDescriptor: file_test_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_proto_goTypes,
DependencyIndexes: file_test_proto_depIdxs,
MessageInfos: file_test_proto_msgTypes,
}.Build()
File_test_proto = out.File
file_test_proto_rawDesc = nil
file_test_proto_goTypes = nil
file_test_proto_depIdxs = nil
}
La función principal es la lógica empresarial principal. Aquí hay una demostración de serialización y deserialización:
package main
import (
"fmt"
"grpc_learn/pb"
"github.com/golang/protobuf/proto"
)
func main() {
p1 := pb.Person{
Name: "Jack",
Age: 34,
}
fmt.Printf("person: %+v\n", p1)
buffer, _ := proto.Marshal(&p1)
fmt.Println("序列化person: ", buffer)
var person pb.Person
proto.Unmarshal(buffer, &person)
fmt.Printf("反序列化person: %+v\n", person)
}