protobuf你学废了么?

2023/04/01 grpc

说到云原生,大家应该听说过grpc,protobuf等技术,那今天呢我们认识以下protobuf。

一、protobuf是什么?

1、ProtoBuf(Protocol Buffers)是一种跨平台、语言无关、可扩展的序列化结构数据的方法,可用于网络数据交换及存储。

2、在序列化结构化数据的机制中,ProtoBuf是灵活、高效、自动化的,相对常见的XML、JSON,描述同样的信息,
ProtoBuf序列化后数据量更小、序列化/反序列化速度更快、更简单。

3、一旦定义了要处理的数据的数据结构之后,就可以利用ProtoBuf的代码生成工具生成相关的代码。
只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)
或从各种不同流中对你的结构化数据轻松读写。

protobuf可以对我们定义的消息进行序列化和反序列化

二、为什么要使用protobuf?

1、效率高

   直白点就是使用ProtoBuf对定义的消息进行序列化后,比json,xml存储相同的消息,所占用的空间要小得多。

 (1)从序列化后的数据体积角度,与XML、JSON这类文本协议相比,ProtoBuf通过T-(L)-V(TAG-LENGTH-VALUE)方式编码,不需要", {, }, 
:等分隔符来结构化信息,同时在编码层面使用varint压缩,所以描述同样的信息,ProtoBuf序列化后的体积要小很多,在网络中传输消耗的网络流量更少,
进而对于网络资源紧张、性能要求非常高的场景,ProtoBuf协议是不错的选择。

 (2)从序列化/反序列化速度角度,与XML、JSON相比,ProtoBuf序列化/反序列化的速度更快,比XML要快20-100倍。 

2、支持跨平台、多语言

ProtoBuf是平台无关的,无论是Android与PC,还是C#与Java都可以利用ProtoBuf进行无障碍通讯。

proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#。

通过protoc编译器指定对应语言插件来编译*.proto文件,可生成对应的语言代码。

3、扩展性、兼容性好

具有向后兼容的特性,更新数据结构以后,老版本依旧可以兼容,这也是ProtoBuf诞生之初被寄予解决的问题。因为编译器对不识别的新增字段会跳过不处理。

4、使用简单

ProtoBuf 提供了一套编译工具,可以自动生成序列化、反序列化的样板代码,这样开发者只要关注业务数据idl,简化了编码解码工作以及多语言交互的复杂度。    

5、缺点是

可读性差,缺乏自描述

XML,JSON是自描述的,而ProtoBuf则不是。

ProtoBuf是二进制协议,编码后的数据可读性差,如果没有idl文件,就无法理解二进制数据流,对调试不友好。

不过Charles已经支持ProtoBuf协议,导入数据的描述文件即可,详情可参考Charles Protocol Buffers

此外,由于没有idl文件无法解析二进制数据流,ProtoBuf在一定程度上可以保护数据,提升核心数据被破解的门槛,降低核心数据被盗爬的风险。

三、如何使用protobuf?

使用protobuf呢需要经过以下几个步骤:
1、创建 .proto 文件,定义需要操作的数据结构(消息)
使用 ProtoBuf ,首先需要通过 ProtoBuf 语法定义数据结构(消息),这些定义好的数据结构保存在.proto为后缀的文件中。

2、需要使用protoc编译器结合go插件(protoc-gen-go)生成对应语言的代码。(这里生成的是go代码)

3、在代码中使用ProtoBuf对数据进行序列化和反序列化(这个才是最终的目的)


1、新建proto文件

我们先要定义消息的数据结构

syntax = "proto3"; // proto版本

package protos; // 指定包名,默认go中包名也是这个

option go_package = "./;protos";


// this is a comment
message Student {
  string name = 1;
  bool male = 2;
  repeated int32 scores = 3;
}

2、安装protoc编译器

定义完消息结构,我们要想操作定义的消息(数据结构)那就需要安装protoc编译器,用它来编译 我们的文件,生成对应的消息(数据结构)代码。

安装Protobuf编译器protoc: 用于编译.proto 文件
mac 下安装 
brew install protobuf@3.6

其他环境安装
https://grpc.io/docs/protoc-installation/

安装包不同版本下载地址
https://github.com/protocolbuffers/protobuf/releases


安装完成,查看版本

protoc --version

3、安装protoc-gen-go插件

在生成对应的消息(数据结构)我们需要在 Golang 中使用 protobuf,还需要安装 protoc-gen-go, 这个工具用来将 .proto 文件转换为对应的 Golang 代码。

注意:
Go语言的protobuf插件和runtime library有过2个版本:

第1个版本开源地址:https://github.com/golang/protobuf,包含有插件proto-gen-go,可以生成xx.pb.go和xx_grpc.pb.go。Go工程里导入该版本的protobuf runtime的方式如下:

import "github.com/golang/protobuf"
第2个版本开源地址:https://github.com/protocolbuffers/protobuf-go,同样包含有插件proto-gen-go。不过该项目的proto-gen-go从v1.20版本开始,不再支持生成gRPC服务定义,
也就是xx_grpc.pb.go文件。要生成gRPC服务定义需要使用grpc-go里的progo-gen-go-grpc插件。Go工程里导入该版本的protobuf runtime的方式如下:

import "google.golang.org/protobuf"
proto-gen-go从v1.20版本开始,不再支持生成gRPC服务定义,也就是xx_grpc.pb.go文件

这里我使用第二种方式安装生成xx_grpc.pb.go文件的插件 proto-gen-go

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

4、将proto文件生成xxx.pd.go文件

protoc –go_out=. student.proto

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 comment
type Student struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name   string  `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Male   bool    `protobuf:"varint,2,opt,name=male,proto3" json:"male,omitempty"`
	Scores []int32 `protobuf:"varint,3,rep,packed,name=scores,proto3" json:"scores,omitempty"`
}

5、通过protobuf将数据序列化

package main

import (
	"github.com/echo-music/go-learn/grpc/example/protos"
	"google.golang.org/protobuf/proto"
	"log"
)

func main() {

	test := &protos.Student{
		Name:   "zhangsan",
		Male:   false,
		Scores: []int32{8, 5, 12},
	}

	//序列化
	data, err := proto.Marshal(test)
	if err != nil {
		panic(err)
	}

	newTest := &protos.Student{}
	//反序列化
	err = proto.Unmarshal(data, newTest)
	if err != nil {
		log.Fatal("unmarshaling error: ", err)
	}

	if test.GetName() != newTest.GetName() {
		log.Fatalf("data mismatch %q != %q", test.GetName(), newTest.GetName())
	}

}

资料: https://www.jianshu.com/p/a24c88c0526a

Search

    Table of Contents