声明:本文大部分内容翻译自官方英文文档,其中可能穿插着加入自己的语言用以辅助理解,本文禁止转载。

一、什么是protocol buffers

Protocol buffers是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,与XML相比,Protocol buffers序列化后的码流更小、速度更快、操作更简单。你只需要将要被序列化的数据结构定义一次(译注:使用.proto文件定义),便可以使用特别生成的源代码(译注:使用protobuf提供的生成工具)轻松的使用不同的数据流完成对这些结构数据的读写操作,即使你使用不同的语言(译注:protobuf的跨语言支持特性)。你甚至可以更新你的数据结构的定义(译注:就是更新.proto文件内容)而不会破坏依赖“老”格式编译出来的程序。

二、protocol buffers的工作流程

首先,你需要通过在.proto文件中定义protocol buffer的message类型来指定你想要序列化的数据结构,每一个protocol buffer message是一个逻辑上的信息记录,它包含一系列的键值对。这里展示一个最基本的.ptoto文件的例子,它定义了一个包含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类型都有一个或多个带有唯一编号的字段,每一个字段有一个字段名和一个字段类型,字段类型可以是数值类型(比如整形或浮点型)、booleans(布尔类型)、strings(字符串类型)、raw bytes、甚至(正如上面的例子)还可以是其他的protocol buffer message类型,这允许你可以分层次的组织你的数据结构。你可以单独指定每一个字段为optional fields(可选字段)、required fields(必须字段)、repeated fields(可重复字段)。下一篇博文将会对.proto文件进行更详细的描述。

一旦定义了你的message,你就可以根据你所使用的语言(译注:如JAVA、C++、Python等)使用protocol buffer提供的编译工具编译.proto文件生成数据访问类。这些类为每一个字段都提供了简单的访问器(比如name()和set_name()),同时还提供了将整个结构化数据序列化为原始字节数据以及从原始字节数据反序列化为结构化数据的方法(译注:C++中称之为函数)。例如,如果你使用的语言是C++,运行编译器编译上述的例子将生成一个名为Person的类,在你的应用程序中你可以使用这个类来填充、序列化和反序列化Person protocol buffer messages。之后你可能会写下如下类似的代码(译注:序列化):

 Person person;
person.set_name("John Doe");
person.set_id();
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?

相对于XML,protocol buffers在序列化结构数据时拥有许多先进的特性:

1、更简单

2、序列化后字节占用空间比XML少3-10倍

3、序列化的时间效率比XML快20-100倍

4、具有更少的歧义性

5、自动生成数据访问类方便应用程序的使用

举个例子,如果你想描述一个具有name和email的person数据结构,在XML中,你需要这样做:

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

然而,在protocol buffers的message中(protocol buffers的文本格式)你需要这样做:

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

当这个message被编码成protocol buffer的二进制格式(上述的文本格式只是为了方便阅读、调试和编辑),它将可能占用28个字节长度并且仅需要100-200纳秒的解析时间。相比,XML版本的则至少需要占用69字节的空间(这是在移除XML中的空格、换行之后),同时,将耗费大约5000-10000纳秒的解析时间。

除此之外,手动操作protocol buffer更为方便,例如如下C++代码:

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

然而如果你使用XML,那么你将需要这样做:

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

事物总有两面性,和XML相比protocol buffers并不总是更好的选择,例如,protocol buffers并不适合用来描述一个基于文本的标记型文档(比如HTML),因为你无法轻易的交错文本的结构。另外,XML具有很好的可读性和可编辑性;而protocol buffers,至少在它们的原生形式上并不具备这个特点。XML同时也是可扩展、自描述的。而一个protocol buffer只有在具有message 定义(在.proto文件中定义)时才会有意义。

四、如何开始使用protocol buffers?

首先,可以在这里下载安装包或者源码包

https://developers.google.com/protocol-buffers/docs/downloads#release-packages

这包含了针对JAVA、Python和C++编译器的完整源码,同时包含了你所需要的I/O和测试类。为了完成编译和安装,请参照README文件。

一旦你完成了编译和安装,那么就可以开始使用protocol buffers了,后续的博文将会对C++和JAVA语言的具体使用细节进行阐述。

五、proto3介绍

我们最新的版本version 3 alpha release引进了一个新的语言版本--Protocol Buffers version 3 (称之为proto3),它在我们现存的语言版本(proto2)上引进了一些新特性。proto3简化了protocol buffer language,这使其可以更便于使用和支持更多的编程语言:我们现在的alpha release版本可以让你能产生JAVA、C++、Pthyon、JavaNano、Ruby、Objective-C和C#版本的protocol buffer code,不过可能有时会有一些局限性。另外,你可以使用最新的Go protoc插件来产生Go语言版本的proto3 code,这可以从golang/protobuf Github repository获取。

我们现在只推荐你使用proto3:

1、如果你想尝试在我们新支持的语言中使用protocol buffers

2、如果你想尝试我们最新开源的RPC实现gRPC(目前仍处于alpha release版本),我们建议你为所有的gRPC 服务器和客户端都使用proto3以避免兼容性问题。

注意两个版本的语言APIs并不是完全兼容的,为了避免给原来的用户造成不便,我们将会继续维护之前的那个版本(译注:proto2)。

 六、最后说一点历史

Protocol buffers最初被Google开发用来作为处理索引服务器的request/response协议。在protocol buffers诞生之前,有一个需要手动编码/解码requests、responses的协议,这个协议支持一个数字版本号,这导致了一个非常丑陋的代码,如下所示:

 if (version == ) {
...
} else if (version > ) {
if (version == ) {
...
}
...
}

很显然的,格式化的协议也导致了复杂的新版本推出问题,因为开发人员必须确保所有服务器请求的发起者和实际的请求处理者之间都要理解新的协议。

Protocol buffers就是用来解决这些问题的:

1、可以很容易的插入新字段,中间的服务器可以简单的解析它而不需要了解所有字段。

2、格式更具有自描述性,可以被不同的语言处理(比如JAVA、C++、Python等)。

3、自动产生序列化和反序列化代码从而避免了手动解析。

4、除了应用在具有短暂生命周期的RPC请求中,人们开始使用protocol buffers 作为一种便利的自描述格式来存储数据(比如在Bigtable中)。

5、服务器的RPC接口开始被声明为协议文件的一部分,通过protocol 编译器产生stub类,该类可以被用户根据实际实现的服务器接口进行重写。

protobuf中文教程(第一篇)的更多相关文章

  1. (转) 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)

    一.spring cloud简介 spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选.分布式会话等等.它运 ...

  2. EnjoyingSoft之Mule ESB开发教程第一篇:初识Mule ESB

    目录 1. Mule ESB基本介绍 2. Mule ESB社区版和企业版 3. Mule ESB常用场景 4. Mule ESB软件安装 客户端安装 服务端安装 5. 第一个Mule ESB应用- ...

  3. spring cloud系列教程第一篇-介绍

    spring cloud系列教程第一篇-介绍 前言: 现在Java招聘中最常见的是会微服务开发,微服务已经在国内火了几年了,而且也成了趋势了.那么,微服务只是指spring boot吗?当然不是了,微 ...

  4. SpringCloud 教程 | 第一篇: 服务的注册与发现Eureka(转载)

    SpringCloud 教程 | 第一篇: 服务的注册与发现Eureka(Finchley版本) 转载请标明出处:http://blog.csdn.net/forezp/article/details ...

  5. Swing:LookAndFeel 教程第一篇——手把手教你写出自己的 LookAndFeel

    本文是 LookAndFeel 系列教程的第一篇. 是我在对 Swing 学习摸索中的一些微薄经验. 我相信,细致看全然系列之后.你就能写出自己的 LookAndFeel. 你会发现 Swing 原来 ...

  6. EbitenCookBook中文教程 第一课:安装 Ebiten

    本文实时更新原址:https://ebitencookbook.vercel.app/docs/CookBook_Start/class1 第一课 安装 Ebiten 欢迎大家来到 Ebiten 中文 ...

  7. spring Boot+spring Cloud实现微服务详细教程第一篇

    前些天项目组的大佬跟我聊,说项目组想从之前的架构上剥离出来公用的模块做微服务的开发,恰好去年的5/6月份在上家公司学习了国内开源的dubbo+zookeeper实现的微服务的架构.自己平时对微服务的设 ...

  8. SpringCloud 教程 | 第一篇: 服务的注册与发现Eureka(Finchley版本)

    一.spring cloud简介 鉴于<史上最简单的Spring Cloud教程>很受读者欢迎,再次我特意升级了一下版本,目前支持的版本为Spring Boot版本2.0.3.RELEAS ...

  9. 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现Eureka(Finchley版本)

    转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f1-eureka/ 本文出自方志朋的博客 一.spring ...

随机推荐

  1. IOS之Objective-C学习 策略模式

    对于策略模式,我个人理解策略模式就是对各种规则的一种封装的方法,而不仅仅是对算法的封装与调用而已.与工厂模式中简单工厂有点类似,但是比简单工厂更有耦合度,因为策略模式以相同的方法调用所有的规则,减少了 ...

  2. Oracle Partition Outer Join 稠化报表

    partition outer join实现将稀疏数据转为稠密数据,举例: with t as (select deptno, job, sum(sal) sum_sal from emp group ...

  3. Warm myself by my hand

    周末的尾巴了. 前几天白日里的气温降到10摄氏度以下,穿上了秋裤.隔天跑一次步,晚上九点多,5公里,25分钟左右.换上薄薄的运动裤,两件运动衣.一出宿舍门就没觉得冷,跑着跑着就愈加热了起来.遇到的问题 ...

  4. 使用maven给spring项目打可直接运行的jar包(配置文件内置外置的打法)

    从网上看过许多打jar包的例子,大多是将配置文件打进jar包的.经过本人一番研究,终于搞清楚了怎样将jar包的配置文件外置. 废话不说,直接上spring的pom.xml的配置文件. <proj ...

  5. Unity性能优化(3)-官方教程Optimizing garbage collection in Unity games翻译

    本文是Unity官方教程,性能优化系列的第三篇<Optimizing garbage collection in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  6. python爬虫学习(7) —— 爬取你的AC代码

    上一篇文章中,我们介绍了python爬虫利器--requests,并且拿HDU做了小测试. 这篇文章,我们来爬取一下自己AC的代码. 1 确定ac代码对应的页面 如下图所示,我们一般情况可以通过该顺序 ...

  7. xcode8权限以及相关设置

    我们需要打开info.plist文件添加相应权限的说明,否则程序在iOS10上会出现崩溃. 具体如下图: QQ20160914-0.png 注意,添加的时候,末尾不要有空格麦克风权限:Privacy ...

  8. js变量问题

    this指向问题,谁调用它,它就指谁!!! 1.var foo = 1; function bar() { foo = 10; return; function foo() {} } bar(); a ...

  9. SQL操作符

    Oracle中的操作符算术操作符:无论是在sqlserver,或者是java中,每种语言它都有算术操作符,大同小异. Oracle中算术操作符(+)(-)(*)(/) 值得注意的是:/ 在oracle ...

  10. 百度地图demo

    以下代码拷贝成html,直接运行即能看到百度地图 <!DOCTYPE html><html> <head> <meta http-equiv="Co ...