ProtoBuf 作为一种跨平台、语言无关、可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储。随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代Restful之势,而 ProtoBuf 作为g RPC 跨语言、高性能的法宝,我们技术人有必要

深入理解 ProtoBuf 原理,为以后的技术更新和选型打下基础。

我将过去的学习过程以及实践经验,总结成系列文章,与大家一起探讨学习,希望大家能有所收获,当然其中有不正确的地方也欢迎大家批评指正。

本系列文章主要包含:

  1. 深入理解 ProtoBuf 原理与工程实践(概述)
  2. 深入理解 ProtoBuf 原理与工程实践(编码)
  3. 深入理解 ProtoBuf 原理与工程实践(序列化)
  4. 深入理解 ProtoBuf 原理与工程实践(工程实践)

一、什么是ProtoBuf

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

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

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

二、为什么是 ProtoBuf

大家可能会觉得 Google 发明 ProtoBuf 是为了解决序列化速度的,其实真实的原因并不是这样的。

ProtoBuf最先开始是 Google用来解决索引服务器 request/response 协议的。没有ProtoBuf之前,Google 已经存在了一种 request/response 格式,用于手动处理 request/response 的编解码。它也能支持多版本协议,不过代码不够优雅:

if (protocolVersion=1) {doSomething();
} else if (protocolVersion=2) {doOtherThing();
} ...

如果是非常明确的格式化协议,会使新协议变得非常复杂。因为开发人员必须确保请求发起者与处理请求的实际服务器之间的所有服务器都能理解新协议,然后才能切换开关以开始使用新协议。

这也就是每个服务器开发人员都遇到过的低版本兼容、新旧协议兼容相关的问题。

为了解决这些问题,于是ProtoBuf就诞生了。

ProtoBuf 最初被寄予以下 2 个特点:

  • 更容易引入新的字段,并且不需要检查数据的中间服务器可以简单地解析并传递数据,而无需了解所有字段。
  • 数据格式更加具有自我描述性,可以用各种语言来处理(C++, Java 等各种语言)。

这个版本的 ProtoBuf 仍需要自己手写解析的代码。

不过随着系统慢慢发展,演进,ProtoBuf具有了更多的特性:

  • 自动生成的序列化和反序列化代码避免了手动解析的需要。(官方提供自动生成代码工具,各个语言平台的基本都有)。
  • 除了用于数据交换之外,ProtoBuf被用作持久化数据的便捷自描述格式。

ProtoBuf 现在是 Google 用于数据交换和存储的通用语言。谷歌代码树中定义了 48162 种不同的消息类型,包括 12183 个 .proto 文件。它们既用于 RPC 系统,也用于在各种存储系统中持久存储数据。

ProtoBuf 诞生之初是为了解决服务器端新旧协议(高低版本)兼容性问题,名字也很体贴,“协议缓冲区”。只不过后期慢慢发展成用于传输数据。

Protocol Buffers 命名由来:

Why the name "Protocol Buffers"?

The name originates from the early days of the format, before we had the protocol buffer compiler to generate classes for us. At the time, there was a class called ProtocolBuffer which actually acted as a buffer for an individual method. Users would add tag/value pairs to this buffer individually by calling methods like AddValue(tag, value). The raw bytes were stored in a buffer which could then be written out once the message had been constructed.

Since that time, the "buffers" part of the name has lost its meaning, but it is still the name we use. Today, people usually use the term "protocol message" to refer to a message in an abstract sense, "protocol buffer" to refer to a serialized copy of a message, and "protocol message object" to refer to an in-memory object representing the parsed message.

三、如何使用 ProtoBuf

3.1 ProtoBuf 协议的工作流程

可以看到,对于序列化协议来说,使用方只需要关注业务对象本身,即 idl 定义,序列化和反序列化的代码只需要通过工具生成即可。

文章福利:现在C++程序员面临的竞争压力越来越大。那么,作为一名C++程序员,怎样努力才能快速成长为一名高级的程序员或者架构师,或者说一名优秀的高级工程师或架构师应该有怎样的技术知识体系,这不仅是一个刚刚踏入职场的初级程序员,也是工作三五年之后开始迷茫的老程序员,都必须要面对和想明白的问题。为了帮助大家少走弯路,技术要做到知其然还要知其所以然。以下视频获取点击:C++架构师学习资料

如果想学习C++工程化、高性能及分布式、深入浅出。性能调优、TCP,协程,Nginx源码分析Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,Linux内核,P2P,K8S,Docker,TCP/IP,协程,DPDK的朋友可以看一下这个学习地址:C/C++Linux服务器开发高级架构师/Linux后台架构师

3.2 ProtoBuf 消息定义

ProtoBuf 的消息是在idl文件(.proto)中描述的。下面是本次样例中使用到的消息描述符customer.proto:

syntax = "proto3";package domain;option java_package = "com.protobuf.generated.domain";
option java_outer_classname = "CustomerProtos";message Customers {repeated Customer customer = 1;
}message Customer {int32 id = 1;string firstName = 2;string lastName = 3;enum EmailType {PRIVATE = 0;PROFESSIONAL = 1;}message EmailAddress {string email = 1;EmailType type = 2;}repeated EmailAddress email = 5;
}

上面的消息比较简单,Customers包含多个Customer,Customer包含一个id字段,一个firstName字段,一个lastName字段以及一个email的集合。

除了这些定义外,文件顶部还有三行可帮助代码生成器:

  1. 首先,syntax = "proto3"用于idl语法版本,目前有两个版本proto2和proto3,两个版本语法不兼容,如果不指定,默认语法是proto2。由于proto3比proto2支持的语言更多,语法更简洁,本文使用的是proto3。
  2. 其次有一个package domain;定义。此配置用于嵌套生成的类/对象。
  3. 有一个option java_package定义。生成器还使用此配置来嵌套生成的源。此处的区别在于这仅适用于Java。在使用Java创建代码和使用JavaScript创建代码时,使用了两种配置来使生成器的行为有所不同。也就是说,Java类是在包com.protobuf.generated.domain下创建的,而JavaScript对象是在包domain下创建的。

ProtoBuf 提供了更多选项和数据类型,本文不做详细介绍,感兴趣可以参考这里。

3.3 代码生成

首先安装 ProtoBuf 编译器 protoc,这里有详细的安装教程,安装完成后,可以使用以下命令生成 Java 源代码:

protoc --java_out=./src/main/java ./src/main/idl/customer.proto

从项目的根路径执行该命令,并添加了两个参数:java_out,定义./src/main/java/为Java代码的输出目录;而./src/main/idl/customer.proto是.proto文件所在目录。

生成的代码非常复杂,但是幸运的是它的用法却非常简单。

CustomerProtos.Customer.EmailAddress email = CustomerProtos.Customer.EmailAddress.newBuilder().setType(CustomerProtos.Customer.EmailType.PROFESSIONAL).setEmail("crichardson@email.com").build();CustomerProtos.Customer customer = CustomerProtos.Customer.newBuilder().setId(1).setFirstName("Lee").setLastName("Richardson").addEmail(email).build();// 序列化byte[] binaryInfo = customer.toByteArray();System.out.println(bytes_String16(binaryInfo));System.out.println(customer.toByteArray().length);// 反序列化CustomerProtos.Customer anotherCustomer = CustomerProtos.Customer.parseFrom(binaryInfo);System.out.println(anotherCustomer.toString());

3.4 性能数据

我们简单地以Customers为模型,分别构造、选取小对象、普通对象、大对象进行性能对比。

序列化耗时以及序列化后数据大小对比

反序列化耗时

四、总结

上面介绍了 ProtoBuf 是什么、产生的背景、基本用法,我们再总结下。

优点:

1. 效率高

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

// 我们简单做个对比
// 要描述如下JSON数据
{"id":1,"firstName":"Chris","lastName":"Richardson","email":[{"type":"PROFESSIONAL","email":"crichardson@email.com"}]}
# 使用JSON序列化后的数据大小为118byte
7b226964223a312c2266697273744e616d65223a224368726973222c226c6173744e616d65223a2252696368617264736f6e222c22656d61696c223a5b7b22747970<br>65223a2250524f46455353494f4e414c222c22656d61696c223a226372696368617264736f6e40656d61696c2e636f6d227d5d7d
# 而使用ProtoBuf序列化后的数据大小为48byte
0801120543687269731a0a52696368617264736f6e2a190a156372696368617264736f6e40656d61696c2e636f6d1001

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

2. 支持跨平台、多语言

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

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

3. 扩展性、兼容性好

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

4. 使用简单

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

缺点:

可读性差,缺乏自描述

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

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

不过Charles已经支持ProtoBuf协议,导入数据的描述文件即可。

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

深入理解 ProtoBuf 原理与工程实践相关推荐

  1. 深入理解 ProtoBuf 原理与工程实践(概述)

    ProtoBuf 作为一种跨平台.语言无关.可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储.随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代R ...

  2. 人脸识别技术原理与工程实践

    1人脸识别应用场景(验证) 我们先来看看人脸识别的几个应用.第一个是苹果的FACE ID,自从苹果推出FaceID后,业界对人脸识别的应用好像信心大增,各种人脸识别的应用从此开始"野蛮生长& ...

  3. 人脸识别技术原理与工程实践(10个月人脸识别领域实战总结)

    1人脸识别应用场景(验证) 我们先来看看人脸识别的几个应用.第一个是苹果的FACE ID,自从苹果推出FaceID后,业界对人脸识别的应用好像信心大增,各种人脸识别的应用从此开始"野蛮生长& ...

  4. 卡尔曼滤波原理与工程实践

    卡尔曼滤波是非常经典的预测追踪算法,能够在系统存在噪声干扰的情况下进行系统状态的最优估计,广泛使用在导航.制导.控制相关的领域. 1. 一个简单的场景 假设我们开发了一台无人机(假设它的名字是Eva) ...

  5. AES加密原理和AOE工程实践

    AES加密原理和AOE工程实践 在AI业务的开发的过程中,我们常常需要对模型文件进行加密. 我们从以下几个方面来说一说AES的加密原理以及AOE里的工程实践. 常见的加密算法 AOE对模型加密需求的思 ...

  6. 深入理解FFM原理与实践

    原文:http://tech.meituan.com/deep-understanding-of-ffm-principles-and-practices.html 深入理解FFM原理与实践 del2 ...

  7. 《深入理解Kafka:核心设计与实践原理》笔误及改进记录

    2019年2月下旬笔者的有一本新书--<深入理解Kafka:核心设计与实践原理>上架,延续上一本<RabbitMQ实战指南>的惯例,本篇博文用来记录现在发现的一些笔误,一是给购 ...

  8. 分级加权评分算法 java_荐书|智能风控:原理、算法与工程实践

    图书简介 风控领域是新兴的机器学习应用场景之一,其特点包括了负样本占比极少.业务对模型解释性要求偏高.业务模型多样.风控数据源丰富等. <智能风控:原理.算法与工程实践>一书共 8 章,包 ...

  9. JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·抽象工厂——理解“开放封闭”

    JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·抽象工厂--理解"开放封闭" 一个不简单的简单工厂引发的命案 在实际的业务中,我们往往面对的复杂度并非数个类 ...

最新文章

  1. linux 禁止内部命令,Linux命令——文件内部命令
  2. 洛谷P1467 循环数 Runaround Numbers
  3. 计算机网络按功能自底而上划分,大连理工大学2011计算机期末模拟题3
  4. JS-JavaScript学习笔记(一)
  5. 查看mongodb数据路径_Mac OS 中安装和使用 MongoDB 的方法
  6. Git - 版本控制工具十分钟入门手册
  7. Python 爬虫进阶五之多线程的用法
  8. 一行c语言代码,打钩的一行c语言代码解释一下,谢谢,详细解释绝对最佳
  9. 浏览器禁用Cookie,基于Cookie的会话跟踪机制失效的解决的方法
  10. STM32F103/429串口IAP+Ymodem升级
  11. JVM垃圾收集器(2)
  12. 1.2 安装与卸载Visual Studio 2018
  13. 联想硬盘保护系统计算机名,联想硬盘保护系统的使用方法
  14. win10突然不能使用usb大容量存储设备(移动硬盘)的解决方法
  15. linux安装启动openoffice和swftools
  16. Qt for Mac苹果开发中,使用Apple Developer文档
  17. 基于MATLAB的TODA定位算法的仿真
  18. 论文笔记 Digging into self-supervised monocular depth estimation
  19. 《崩坏3》评测:游戏设计中整体性和利用率分析(上)
  20. java超大数整除7,Java编写程序:求1-100之间可以被7整除的数的个数,并输出这些数。求大佬...

热门文章

  1. 一些关于面试常考的问题总结(计算机网络和python语法)
  2. 技术主管和项目经理一定要读的 6本书!
  3. ardupilot罗盘校准代码分析
  4. freesurfer分割后的解剖文件.annot, 如何求解剖区域的三维坐标?如已知lh.HCP-MMP1.annot,如何求Glasser360的皮层三维坐标?
  5. 安卓 二维码扫描(ZXing)
  6. 半导体八大工艺流程图_半导体行业“香饽饽”:别再错过台积电、英伟达、高通...
  7. 关闭 TSVNCache.exe 进程
  8. datetime报错 sql脚本_Linux中Mysql数据库备份shell脚本编写实例
  9. MEMS运动传感器:三轴数字输出陀螺仪——L3GD20
  10. Nuendo Pro 10.2.10 x64 WiN 音乐制作编曲宿主软件下载