简介

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. vux使用

    Vue中使用vux的配置,分为两种情况: 一.根据vux文档直接安装,无需手动配置 npm install vue-cli -g // 如果还没安装 vue init airyland/vux2 my ...

  2. 算法型稳定币USDN有什么价值和用途?

    USDN的标签是"数字美元",与大多数稳定资产一样,USDN是一种金融服务产品.基于NGK公链发行的算法型稳定币USDN,USDN是和美元1:1锚定的加密数字货币,1USDN等于1 ...

  3. Codeforces 1485F Copy or Prefix Sum

    题目链接 点我跳转 题目大意 给定一个长度为 \(N\) 的序列 \(bi\) 问有多少个长度为 \(N\) 的序列 \(a\) 使得 \(b[i] = a[i]\) 或 \(b[i] = ∑a[j] ...

  4. 手把手教你Spring Boot整合Mybatis Plus 代码生成器

    一.在pom.xml中添加所需依赖 <!-- MyBatis-Plus代码生成器--> <dependency> <groupId>com.baomidou< ...

  5. SpringCloud之服务降级

    1.Hystrix(断路器) 1.1定义 扇出:多个微服务调用的时候,假设微服务A调用微服务B和C,微服务B和C又调用其他的服务.服务雪崩:如果扇出的链路上某个微服务的调用时间过长或不可用,对微服务A ...

  6. Guava-RateLimiter实现令牌桶控制接口限流方案

    一.前言 对于一个应用系统来说,我们有时会遇到极限并发的情况,即有一个TPS/QPS阀值,如果超了阀值可能会导致服务器崩溃宕机,因此我们最好进行过载保护,防止大量请求涌入击垮系统.对服务接口进行限流可 ...

  7. oracle can't kill session

    oracle 在杀会话时,会出现杀不掉的情况. 原因是在回滚大事物   解决方法: alter system disconnect session 'sid, serial#' immediate; ...

  8. bootstrap日期范围选择插件daterangepicker详细使用方法

    插件官方网站地址 bootstrap-daterangepicker是个很方便的插件,但是对我这种菜鸟来说,文档不够详细,摆弄了好久才整好.记录下来供以后参考,也希望能帮到有需要的朋友. 目前版本是2 ...

  9. Git:分支管理

    代码中至少有一个分支,就是主分支master,默认都是在主分支上开发. 多分支 分支名: 版本库中必须唯一 不能以 - 开头 可以试用/,但不能以/结尾,被/分隔的名称不能以.开头 不能有连个连续的 ...

  10. Nginx配置翻译

    Windows 格式 server { listen 82; server_name localhost; root "D:/testfile/"; location / { in ...