简介

Protocol Buffers是什么?

protocol buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小、更快、更为简单。你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏根据旧数据结构编译而成并且已部署的程序。

1 . 使用protobuf实现节点间通信, 编码报文以提高传输效率;

2 . protobuf全程Protocol Buffers, 是Google开发的一种数据描述语言;

3 . Protobuf是一种轻便高效的结构化数据存储格式;

4 . Protobuf跟存储格式,语言, 平台无关;

5 . protobuf可扩展可序列化;

6 . protobuf以二进制方式存储, 占用内存空间小;

protobuf广泛地应用于远程过程调用(PRC)的二进制传输,使用protobuf的目的是为了获得更高的性能。传输前使用protobuf编码,接收方再进行解码,可显著地降低二进制传输数据的大小。另外,protobuf非常适合传输结构化数据,便于通信字段的扩展。

用途

1 . 可以轻松引入新字段, 中间服务器不需要检查数据, 可以简单解析他并传递数据而无需了解所有字段;

2 . 格式更具有自我描述性, 可以用各种语言处理(C++,Java等)

随着系统发展, 他获得了其他功能和用途:

3 . 自动生成的序列化和反序列化代码避免了手动解析的需要;

**4 . 除了用于短期RPC(远程过程调用)请求之外, 人们还开始使用 protocol buffers作为一种方便的自描述格式, **用于持久存储数据(例如在 Bigtable中);

5 . 服务器RPC接口开始被声明为协议文件的一部分, protocol编译器生成存根类, 用户可以使用服务器接口的实际实现来覆盖这些类;

它是如何工作的?

你可以通过在 .proto 文件中定义 protocol buffer message 类型,来指定你想如何对序列化信息进行结构化。每一个 protocol buffer message 是一个信息的小逻辑记录,包含了一系列的 name-value 对。这里有一个非常基础的 .proto 文件样例,它定义了一个包含 "person" 相关信息的 message:

message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3; enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
} message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
} repeated PhoneNumber phone = 4;
}

正如你所见,message 格式很简单 - 每种 message 类型都有一个或多个具有唯一编号的字段,每个字段都有一个名称和一个值类型,其中值类型可以是数字(整数或浮点数),布尔值,字符串,原始字节,甚至(如上例所示)其它 protocol buffer message 类型,这意味着允许你分层次地构建数据。你可以指定 optional 字段,required 字段和 repeated 字段。 你可以在 Protocol Buffer 语言指南 中找到有关编写 .proto 文件的更多信息。

proto3 已舍弃 required 字段,optional 字段也无法显示使用(因为缺省默认就设置为 optional)

一旦定义了 messages,就可以在 .proto 文件上运行 protocol buffer 编译器来生成指定语言的数据访问类。这些类为每个字段提供了简单的访问器(如 name()和 set_name()),以及将整个结构序列化为原始字节和解析原始字节的方法 - 例如,如果你选择的语言是 C++,则运行编译器上面的例子将生成一个名为 Person 的类。然后,你可以在应用程序中使用此类来填充,序列化和检索 Person 的 messages。于是你可以写一些这样的代码:

Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("jdoe@example.com");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);

之后,我们可以重新读取解析 message

**

fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

你可以在 message 格式中添加新字段,而不会破坏向后兼容性;旧的二进制文件在解析时只是忽略新字段。因此,如果你的通信协议使用 protocol buffers 作为其数据格式,则可以扩展协议而无需担心破坏现有代码。

为什么不适用XML?

对于序列化结构数据, protocol buffers 比XML更具优势, Protocol buffers:

1 . 更简单

2 . 小3 - 10倍

3 . 快20 - 100 倍

4 . 更加清晰明确

5 . 自动生成更易于以编程方式使用的数据访问类;

**

例如:

假设你想要为具有姓名和电子邮件的人建模, 在xml中, 我们需要:

<person>
<name>John Doe</name>
<email>jdoe@example.com</email>
</person>

**

而相对应的Protocol Buffer Message 格式是:

# Textual representation of a protocol buffer.
# This is *not* the binary format used on the wire.
person {
name: "John Doe"
email: "jdoe@example.com"
}

当此消息编码为protocol buffer 二进制格式 时(上面的文本格式只是为了调试和编辑的方便而用人类可读的形式表示),它可能是 28 个字节长,需要大约 100-200 纳秒来解析。如果删除空格,XML版本至少为 69 个字节,并且需要大约 5,000-10,000 纳秒才能解析。

此外,比起 XML,操作 protocol buffer 更为容易:

cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

**    而使用XML, 必须执行如下操作:**

**

cout << "Name: "
<< person.getElementsByTagName("name")->item(0)->innerText()
<< endl;
cout << "E-mail: "
<< person.getElementsByTagName("email")->item(0)->innerText()
<< endl;

但是,protocol buffers 并不总是比 XML 更好的解决方案 - 例如,protocol buffers 不是使用标记(例如 HTML)对基于文本的文档建模的好方法,因为你无法轻松地将结构与文本交错。此外,XML 是人类可读的和人类可编辑的;protocol buffers,至少它们的原生格式,并不具有这样的特点。XML 在某种程度上也是自我描述的。只有拥有 message 定义(.proto文件)时,protocol buffer 才有意义;

准备使用的包

Protoc

protoc是protobuf文件(.proto)的编译器,使用protoc工具可以将.proto文件转换为各种编程语言对应的源码,包含数据类型定义和调用接口等;

https://github.com/protocolbuffers/protobuf/releases 中下载最新的protobuf安装包 protoc-3.15.6-win64.zip

解压压缩包后将bin目录下的protoc.exe文件移动到$GOPATH/bin目录下,注意$GOPATH/bin需要提前添加到环境变量Path目录下;

$ protoc -help
$ protoc --version
libprotoc 3.15.6

Protobuf

protobuf对于Golang有两个可选用的包分别是官方的goprotobuf和gogoprotobuf,gogoprotobuf是完全兼容Google Protobuf的,只是生成的代码质量要比goprotbuf要高。

安装

go get github.com/golang/protobuf/proto
go get github.com/gogo/protobuf/proto

Protoc-gen-go

protoc-gen-go 是 protobuf 编译插件系列中的Go版本,protoc-gen-to 使用Golang编写。

在Golang中使用protobuf需提前安装 protoc-gen-to工具,用于将.proto文件转换为Golang代码。

go get -u github.com/golang/protobuf/protoc-gen-go

protoc-gen-go将自动安装到$GOPATH/bin目录下

protobuf会在.proto文件中定义需要处理的结构化数据,通过protoc工具可将.proto文件转换为C、C++、Golang、Java、Python等多种语言的代码,因此兼容性好且易于使用;

protoc --go_out=. *.proto

命令之后理论上会将当前目录下的所有的.proto文件生成.pb.go文件,但实际测试发现报错,不推荐使用;

Protoc-gen-gogo

gogoprotobuf有两个插件可用分别是protoc-gen-gogo和protoc-gen-gofast,protoc-gen-gogo生成的文件和protoc-gen-go一样性能略快,protoc-gen-gofast生成的Golang文件更为复杂,但性能却高出5~7倍;

安装

go get github.com/gogo/protobuf/protoc-gen-gogo

protoc *.proto --gogo_out=.

protoc-gen-gofast

安装

go get github.com/gogo/protobuf/protoc-gen-gofast

protoc *.proto --gofast_out=.
# 执行后会将当前目录下的所有.proto文件生成.pd.go文件

语法

Protobuf协议规定:使用Protobuf协议进行数据序列化和反序列化操作时,首先需要定义传输数据的格式,并命名以.proto为扩展名的消息定义文件;

使用message定义一个消息;

指定消息字段类型

分配标识符,在消息字段中每个字段都有唯一的一个标识符,最小标识号可以从1开始,最大到536870911。不可以使用[19000 ~ 19999]之间的标号;

指定字段规则,字段修饰符包括required、optional、repeated三种类型,注意required弊大于利;

使用

1 . 按照protobuf语法, 在.proto文件中定义数据结构, 同时使用protoc工具生成Golang代码;

2 . 在项目代码中引用生成的Golang代码;

定义消息类型

syntax = "proto3";

package proto;

message User{
string name = 1;
bool male = 2;
repeated int32 balance = 3;
} protoc *.proto --gogo_out=.

标量类型

Protobuf类型 Golang类型 描述
int32 int32 变长编码,对于负值效率较低。若域可能存在负值可使用sint64替代。
int64 int64 -
uint32 uint32 变长编码
uint64 uint64 变长编码
sint32 int32 变长编码,在负值时比int32高效。
sint64 int64 变长编码,有符号整型值。编码时比int64高效。
fixed32 uint32 固长编码,4个字节,若数值大于2^28则比uint32高效。
fixed64 uint64 固长编码,8个字节,若数值大于2^56则比uint64高效。
sfixed32 int32 固长编码,4个字节。
sfixed64 int64 固长编码,8个字节。
float float32 -
double float64 -
bool bool 默认false
bytes []byte 任意字节序列,长度不超过2^32,默认空数组。
string string UTF8编码或7-bit ASCII编码的文本,长度不超过2^32。

标量类型如果没有被赋值则不会被序列化,解析时会赋予默认值

标量类型 默认值
strings 空字符串
bytes 空序列
bools false
数值类型 0

**

文件

1 . 文件名使用小写下划线的命名风格,例如lower_snake_case.proto;

2 . 每行不超过80个字符;

3 . 使用2个空格缩进;

包名应该和目录结构对应,例如文件在my/package/目录下,则包名为my.package;

消息

1 . 消息名使用首字母大写驼峰风格(CamelCase),例如message PlayerRequest{...};

2 . 字段名使用小写下划线风格,例如string user_id = 1;

3 . 枚举类型中枚举名使用首字母大写驼峰风格,例如enum FooBar,枚举值使用全大写下划线分割的风格(CAPITALS_WITH_UNDERSCORES),例如FOO_DEFAULT = 1;

服务

RPC服务名和方法名均使用首字母大写驼峰风格, 例如 service FooService{rpc GetSomething()};

案例

创建 .proto 文件

cat test1.proto
syntax = "proto3";
package pb; message Player {
string user_id = 1;
string name = 2;
string icon = 3;
int32 point = 4;
int32 seat = 5;
int32 identity = 6;
int32 status = 7;
} # 生成.pd.go文件
protoc test1.proto --gogo_out=.

创建测试代码

package main

import (
"fmt"
"github.com/gogo/protobuf/proto"
"proto_demo1/pb"
) func main() {
player := &pb.Player{
UserId: "1",
Name: "admin",
}
//序列化
buf, err := proto.Marshal(player)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", buf) // [10 1 49 18 5 97 100 109 105 110]
fmt.Printf("%s\n", buf) // 1admin
//反序列化
obj := &pb.Player{}
err = proto.Unmarshal(buf, obj)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", obj) // user_id:"1" name:"admin"
fmt.Printf("%v\n", obj.GetName()) // admin
} /*
result
proto_demo1 % go run main.go
[10 1 49 18 5 97 100 109 105 110] 1admin
user_id:"1" name:"admin"
admin */

Go Protobuf(比xml小3-10倍, 快20-100倍)的更多相关文章

  1. 谷歌出品EfficientNet:比现有卷积网络小84倍,比GPipe快6.1倍

    [导读]谷歌AI研究部门华人科学家再发论文<EfficientNet:重新思考CNN模型缩放>,模型缩放的传统做法是任意增加CNN的深度和宽度,或使用更大的输入图像分辨率进行训练,而使用E ...

  2. 优化临时表使用,SQL语句性能提升100倍

    [问题现象] 线上mysql数据库爆出一个慢查询,DBA观察发现,查询时服务器IO飙升,IO占用率达到100%, 执行时间长达7s左右.SQL语句如下:SELECT DISTINCT g.*, cp. ...

  3. 转--优化临时表使用,SQL语句性能提升100倍

    转自:http://www.51testing.com/html/01/n-867201-2.html [问题现象] 线上mysql数据库爆出一个慢查询,DBA观察发现,查询时服务器IO飙升,IO占用 ...

  4. MySQL 5.7 优化SQL提升100倍执行效率的深度思考(GO)

    系统环境:微软云Linux DS12系列.Centos6.5 .MySQL 5.7.10.生产环境,step1,step2是案例,精彩的剖析部分在step3,step4. 1.慢sql语句大概需要13 ...

  5. 王家林 Spark公开课大讲坛第一期:Spark把云计算大数据速度提高100倍以上

    王家林 Spark公开课大讲坛第一期:Spark把云计算大数据速度提高100倍以上 http://edu.51cto.com/lesson/id-30815.html Spark实战高手之路 系列书籍 ...

  6. 修改一行SQL代码 性能提升了100倍

    在PostgreSQL中修改了一行不明显的代码,把(ANY(ARRAY[...]) 改成 ANY(VALUES(...))),结果查询时间从20s变为0.2s.最初我们学习使用 EXPLAN ANAL ...

  7. IEEEXtreme 10.0 - Playing 20 Questions with an Unreliable Friend

    这是 meelo 原创的 IEEEXtreme极限编程大赛题解 Xtreme 10.0 - Playing 20 Questions with an Unreliable Friend 题目来源 第1 ...

  8. 一行代码让python的运行速度提高100倍

    python一直被病垢运行速度太慢,但是实际上python的执行效率并不慢,慢的是python用的解释器Cpython运行效率太差. “一行代码让python的运行速度提高100倍”这绝不是哗众取宠的 ...

  9. 通过非聚集索引让select count(*) from 的查询速度提高几十倍、甚至千倍

    通过非聚集索引,可以显著提升count(*)查询的性能. 有的人可能会说,这个count(*)能用上索引吗,这个count(*)应该是通过表扫描来一个一个的统计,索引有用吗? 不错,一般的查询,如果用 ...

随机推荐

  1. js repeatify & no for loop

    js repeatify & no for loop js repeatify https://www.sitepoint.com/5-typical-javascript-interview ...

  2. [C语言学习笔记五]复合语句和操作符的区分

    复合语句的概念和用法 在部分时候,语句必须要与其他语句相结合才能实现应有的功能.放在花括号 {} 里的代码叫做复合语句. 例如: int a,b; if (a == b) ... ... /* 这一部 ...

  3. flask启动常见问题1:sqlalchemy.exc.ArgumentError: Mapper mapped class UserCode->data_system_user_email could not assemble any primary key columns for mapped table 'data_system_user_email'

    我的描述:当我编辑好flask以后,ORM映射数据库完成,启动项目时,发生现象: 解决: 看字面的意思是主键导致的错误,于是我查看了data_system_user_email的键参数配置,发现表没有 ...

  4. Maven报错:Unsupported major.minor version 51.0

    这个错误时因为JDK版本的问题,比如本机的JDK为1.6,但是项目编译时用的JDK为1.7那么就会出现这个异常,因为本机JDK版本较低不能执行编译版本为高版本的Class文件,各JDK版本对应的错误编 ...

  5. 使用Docker创建MongoDb服务

    使用Docker创建MongoDb服务 1.先拉mongodb镜像 docker pull mongodb:4.2.5 2.创建映射目录 创建mongo映射目录,用于存放后面的相关东西. mkdir ...

  6. WPF -- DataTemplate与ControlTemplate结合使用

    如深入浅出WPF中的描述,DataTemplate为数据的外衣,ControlTemplate为控件的外衣.ControlTemplate控制控件的样式,DataTemplate控制数据显示的样式,D ...

  7. window下象MAC一样工作的工具

    前面是MAC 后面是windows对应工具,只是做一个列表说明,具体使用自行百度 1.item2 vs Cmder 命令行 2.Homebrew vs Chocolatey 包管理器 3.Spotli ...

  8. nacos服务注册与发现之客户端

    服务注册 1.1 NamingService.registerInstance的方法为客户端提供的服务注册接口 1.2 客户端通过调用NamingService.registerService上报到n ...

  9. Arrays.Sort()中的那些排序算法

    本文基于JDK 1.8.0_211撰写,基于java.util.Arrays.sort()方法浅谈目前Java所用到的排序算法,仅个人见解和笔记,若有问题欢迎指证,着重介绍其中的TimSort排序,其 ...

  10. 【转载】Java虚拟机类加载机制与案例分析

    出处:https://blog.csdn.net/u013256816/article/details/50829596 https://blog.csdn.net/u013256816/articl ...