Protocol Buffers学习笔记

简介

Google Protocol Buffer( 简称 Protobuf)是Google公司内部的混合语言数据标准,他们主要用于RPC系统和持续数据存储系统。java

Protobuf应用场景:

Protocol Buffers 是一种轻便高效的结构化数据存储格式,能够用于结构化数据串行化,或者说序列化。它很适合作数据存储或RPC数据交换格式。可用于通信协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。编程

简单来讲,Protobuf的功能相似于XML,即负责把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议等使用场景。为何已经有了XLM,JSON等已经很广泛的数据传输方式,还要设计出Protobuf这样一种新的数据协议呢?数组

Protobuf优势:
性能好/效率高数据结构

时间维度:采用XML格式对数据进行序列化时,时间消耗上性能尚可;对于使用XML格式对数据进行反序列化时的时间花费上,耗时长,性能差。编程语言

空间维度:XML格式为了保持较好的可读性,引入了一些冗余的文本信息。因此在使用XML格式进行存储数据时,也会消耗空间。分布式

总体而言,Protobuf以高效的二进制方式存储,比XML小3到10倍,快20到100倍。 代码生成机制ide

代码生成机制的含义在Go语言中,能够经过定义结构体封装描述一个对象,并构造一个新的结构体对象。好比定义Person结构体,并存放于Person.go文件:性能

type Person struct{
Name stringAge
intSex int}

在分布式系统中,由于程序代码时分开部署,好比分别为A、B。A系统在调用B系统时,没法直接采用代码的方式进行调用,由于A系统中不存在B系统中的代码。所以,A系统只负责将调用和通讯的数据以二进制数据包的形式传递给B系统,由B系统根据获取到的数据包,本身构建出对应的数据对象,生成数据对象定义代码文件。这种利用编译器,根据数据文件自动生成结构体定义和相关方法的文件的机制被称做代码生成机制。学习

代码生成机制的优势ui

首先,代码生成机制可以极大解放开发者编写数据协议解析过程的时间,提升工做效率;其次,易于开发者维护和迭代,当需求发生变动时,开发者只须要修改对应的数据传输文件内容便可完成全部的修改。

支持“向后兼容”和“向前兼容”

向后兼容:在软件开发迭代和升级过程当中,"后"能够理解为新版本,越新的版本越靠后;而“前”意味着早起的版本或者先前的版本。向“后”兼容便是说当系统升级迭代之后,仍然能够处理老版本的数据业务逻辑。

向前兼容:向前兼容便是系统代码未升级,可是接受到了新的数据,此时老版本生成的系统代码能够处理接收到的新类型的数据。

支持先后兼容是很是重要的一个特色,在庞大的系统开发中,每每不可能统一完成全部模块的升级,为了保证系统功能正常不受影响,应最大限度保证通信协议的向前兼容和向后兼容。

支持多种编程语言 Protobuf不只仅Google开源的一个数据协议,还有不少种语言的开源项目实现。在Google官方发布的Protobuf的源代码中包含了C++、Java、Python三种语言。本系列课程中,咱们学习如何实现Golang语言中的功能实现。

Protobuf 缺点 可读性较差为了提升性能,Protobuf采用了二进制格式进行编码。二进制格式编码对于开发者来讲,是没办法阅读的。在进行程序调试时,比较困难。

缺少自描述 诸如XML语言是一种自描述的标记语言,即字段标记的同时就表达了内容对应的含义。而Protobuf协议不是自描述的,Protobuf是经过二进制格式进行数据传输,开发者面对二进制格式的Protobuf,没有办法知道所对应的真实的数据结构,所以在使用Protobuf协议传输时,必须配备对应的proto配置文件。

协议语法:

message:Protobuf中定义一个数据结构须要用到关键字message,这一点和Java的class,Go语言中的struct相似。标识号:在消息的定义中,每一个字段等号后面都有惟一的标识号,用于在反序列化过程当中识别各个字段的,一旦开始使用就不能改变。标识号从整数1开始,依次递增,每次增长1,标识号的范围为1~(2^99)-1,其中[19000-19999]为Protobuf协议预留字段,开发者不建议使用该范围的标识号;一旦使用,在编译时Protoc编译器会报出警告。

字段规则:字段规则有三种:

一、required:该规则规定,消息体中该字段的值是必需要设置的。

二、optional:消息体中该规则的字段的值能够存在,也能够为空,optional的字段能够根据defalut设置默认值。repeated:消息体中该规则字段能够存在多个(包括0个),该规则对应java的数组或者go语言的slice。

枚举类型:proto协议支持使用枚举类型,和正常的编程语言同样,枚举类型可使用enum关键字定义在.proto文件中:

enum Age{ 
//1,2表明字段顺序male=1;
female=2;
}

字段默认值:.proto文件支持在进行message定义时设置字段的默认值,能够经过default进行设置,以下所示:

message Address {
required sint32 id = 1 [default = 1];
required string name = 2 [default = '北京'];
optional string pinyin = 3 [default = 'beijing'];
required string address = 4;
required bool flag = 5 [default = true];
}

导入:若是须要引用的message是写在别的.proto文件中,能够经过import "xxx.proto"来进行引入:

嵌套:message与message之间能够嵌套定义,好比以下形式:

syntax = "proto2";
package example;
message Person {
required string Name = 1;
required int32 Age = 2;
required string From = 3;
optional Address Addr = 4;
message Address {required sint32 id = 1;
required string name = 2;optional string pinyin = 3;
required string address = 4;}}

message更新规则

message定义之后若是须要进行修改,为了保证以前的序列化和反序列化可以兼容新的message,message的修改须要知足如下规则:

不能够修改已存在域中的标识号。

全部新增添的域必须是 optional 或者 repeated。

非required域能够被删除。可是这些被删除域的标识号不能够再次被使用。

非required域能够被转化,转化时可能发生扩展或者截断,此时标识号和名称都是不变的。

sint32和sint64是相互兼容的。

fixed32兼容sfixed32。 fixed64兼容sfixed64。

optional兼容repeated。发送端发送repeated域,用户使用optional域读取,将会读取repeated域的最后一个元素。

Protobuf 序列化后所生成的二进制消息很是紧凑,这得益于 Protobuf 采用的很是巧妙的 Encoding 方法。接下来看一看Protobuf协议是如何实现高效编码的。


编辑好proto文件后,生成命令:

route_guide.proto

将结构体赋值,并序列化:

msg_test:=&service.Order{   
	OrderId:              proto.Int32(1),   
	Num:                  proto.Int64(1),   
	Timestamp:            proto.String("20191030"),
}

//序列化代码
msg,err:=proto.Marshal(msg_test)//二进制

if err!=nil{   
panic(err)
}

fmt.Println(msg)

反序列化操做:

//反序列化

var order service.Order

err=proto.Unmarshal(msg,&order) //反序列化


if err!=nil{
   panic(err)
   return
}


fmt.Println(order.GetOrderId())
相关文章
相关标签/搜索