0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入剖析ProtoBuf原理与工程实践

Linux爱好者 来源:vivo互联网技术 作者:Li Guanyun 2021-11-16 09:15 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

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

深入理解 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();
}elseif(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 定义,序列化和反序列化的代码只需要通过工具生成即可。

3.2 ProtoBuf 消息定义

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

syntax="proto3";

packagedomain;

optionjava_package="com.protobuf.generated.domain";
optionjava_outer_classname="CustomerProtos";

messageCustomers{
repeatedCustomercustomer=1;
}

messageCustomer{
int32id=1;
stringfirstName=2;
stringlastName=3;

enumEmailType{
PRIVATE=0;
PROFESSIONAL=1;
}

messageEmailAddress{
stringemail=1;
EmailTypetype=2;
}

repeatedEmailAddressemail=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 提供了更多选项和数据类型,本文不做详细介绍,感兴趣可以参考这里[1]

3.3 代码生成

首先安装 ProtoBuf 编译器 protoc,这里[2]有详细的安装教程,安装完成后,可以使用以下命令生成 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.EmailAddressemail=CustomerProtos.Customer.EmailAddress.newBuilder()
.setType(CustomerProtos.Customer.EmailType.PROFESSIONAL)
.setEmail("crichardson@email.com").build();

CustomerProtos.Customercustomer=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.CustomeranotherCustomer=CustomerProtos.Customer.parseFrom(binaryInfo);
System.out.println(anotherCustomer.toString());

3.4 性能数据

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

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

反序列化耗时

c8f14736-44ef-11ec-b939-dac502259ad0.png

更多性能数据可以参考官方 Benchmark[3]

四、总结

上面介绍了 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
7b226964223a312c2266697273744e616d65223a224368726973222c226c6173744e616d65223a2252696368617264736f6e222c22656d61696c223a5b7b2274797065223a2250524f46455353494f4e414c222c22656d61696c223a226372696368617264736f6e40656d61696c2e636f6d227d5d7d
#而使用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协议,导入数据的描述文件即可,详情可参考Charles Protocol Buffers[4]

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

编辑:jq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • XML
    XML
    +关注

    关注

    0

    文章

    188

    浏览量

    34381
  • 编码
    +关注

    关注

    6

    文章

    1015

    浏览量

    56645
  • JSON
    +关注

    关注

    0

    文章

    125

    浏览量

    7696

原文标题:深入理解 ProtoBuf 原理与工程实践(概述)

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    深入剖析NCP13994:高性能半桥谐振转换器控制器

    在电子工程师的设计世界里,一款优秀的控制器就像是一位得力助手,能够为电源转换设计带来诸多便利和高性能表现。今天,我们就来深入剖析安森美(onsemi)的NCP13994,一款专为半桥谐振转换器打造的高性能电流模式控制器。
    的头像 发表于 12-05 11:27 172次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>剖析</b>NCP13994:高性能半桥谐振转换器控制器

    深入剖析onsemi NVMFWS004N10MC N沟道功率MOSFET

    在电子工程师的日常设计工作中,MOSFET 作为关键的功率器件,其性能和特性直接影响着整个电路的运行效率和稳定性。今天,我们就来详细剖析 onsemi 推出的 NVMFWS004N10MC 这款 100V、3.9mΩ、138A 的单 N 沟道功率 MOSFET。
    的头像 发表于 12-01 15:35 97次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>剖析</b>onsemi NVMFWS004N10MC N沟道功率MOSFET

    深入剖析 onsemi NL7SZ19:1 到 2 解码器/多路分配器的卓越性能与应用

    深入剖析 onsemi NL7SZ19:1 到 2 解码器/多路分配器的卓越性能与应用
    的头像 发表于 12-01 13:54 136次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>剖析</b> onsemi NL7SZ19:1 到 2 解码器/多路分配器的卓越性能与应用

    深入剖析ADS5546:高性能14位ADC的卓越之选

    师的首选。今天,我们就来深入剖析这款ADC,了解它的技术细节和应用要点。 文件下载: ads5546.pdf 核心特性,铸就卓越性能 高采样率与高精度 ADS5546具备高达190 MSPS的最大采样率,能够快速准确地采集信号。同时,它拥有14位的分辨率,且无丢失码,确保
    的头像 发表于 11-29 10:41 767次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>剖析</b>ADS5546:高性能14位ADC的卓越之选

    深入剖析Z80C30/Z85C30 CMOS SCC串行通信控制器

    (Serial Communications Controller)串行通信控制器,凭借其卓越的性能和丰富的功能,成为了众多工程师的首选。今天,我们就来深入剖析这款控制器,了解它的特点、功能以及在实际应用中
    的头像 发表于 11-26 16:22 520次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>剖析</b>Z80C30/Z85C30 CMOS SCC串行通信控制器

    CI/CD实践中的运维优化技巧

    在数字化转型的浪潮中,CI/CD已经成为现代软件开发的基石。然而,真正能够发挥CI/CD威力的,往往在于那些不为人知的运维优化细节。本文将深入剖析CI/CD实践中的关键优化技巧,帮助您构建更高效、更稳定的持续集成与部署体系。
    的头像 发表于 09-18 15:05 609次阅读

    深入剖析RabbitMQ高可用架构设计

    在微服务架构中,消息队列故障导致的系统不可用率高达27%!如何构建一个真正可靠的消息中间件架构?本文将深入剖析RabbitMQ高可用设计的核心要点。
    的头像 发表于 08-18 11:19 696次阅读

    深入剖析Docker全链路安全防护策略

    在云原生时代,Docker容器安全已成为运维工程师必须面对的核心挑战。本文将从实战角度深入剖析Docker全链路安全防护策略,涵盖镜像构建、容器运行、网络隔离等关键环节,助你构建企业级安全防护体系。
    的头像 发表于 08-18 11:17 733次阅读

    从线性到非线性:工程师必须掌握的Abaqus三大非线性来源

    硕迪科技为您深入剖析Abaqus三大非线性来源:材料非线性、几何非线性、边界条件非线性。掌握工程师必备的非线性分析核心知识,理解复杂工程问题的本质
    的头像 发表于 08-07 18:08 922次阅读
    从线性到非线性:<b class='flag-5'>工程</b>师必须掌握的Abaqus三大非线性来源

    研华工业AI Agent的发展态势及实践思考

    WISE-IoT 产品总监康宁女士,深入剖析工业 AI Agent 的发展态势及实践思考,期待为您揭开产业智能化跃迁的全新篇章。
    的头像 发表于 06-23 09:31 809次阅读

    从四个方面深入剖析富捷电阻的优势

    理成本的电阻产品系列,为电子行业提供了一个可靠的选择。本文将从产品结构、同业对比、成本分析以及品质保障四个方面深入剖析富捷电阻的优势,展现其如何在激烈的市场竞争中脱颖而出。
    的头像 发表于 05-09 10:47 830次阅读
    从四个方面<b class='flag-5'>深入</b><b class='flag-5'>剖析</b>富捷电阻的优势

    深入剖析智芯传感开口封封装技术

    封装是MEMS制造过程的重要环节,决定了MEMS器件的可靠性和成本。开口封封装技术是智芯传感在封装工艺上的一次创新突破。这一创新技术不仅攻克了MEMS压力传感芯片一体化塑封的这一世界级难题,还凭借其卓越的性能与高效生产优势,引领着行业的技术升级。本文将深入剖析开口封封装技
    的头像 发表于 03-19 10:39 1153次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>剖析</b>智芯传感开口封封装技术

    微软发布《GraphRAG实践应用白皮书》助力开发者

    了其概述、表示、存储、查询与抽取方法,为读者打下了坚实的理论基础。随后,白皮书深入剖析了GraphRAG的整体架构、实践挑战以及图形化展示,并结合Agentic RAG所面临的难题,提出了切实可行的应对方案。 此外,白皮书还通过
    的头像 发表于 01-13 16:11 1381次阅读

    深入剖析半导体湿法刻蚀过程中残留物形成的机理

    半导体湿法刻蚀过程中残留物的形成,其背后的机制涵盖了化学反应、表面交互作用以及侧壁防护等多个层面,下面是对这些机制的深入剖析: 化学反应层面 1 刻蚀剂与半导体材料的交互:湿法刻蚀技术依赖于特定
    的头像 发表于 01-08 16:57 1375次阅读

    BNC接头技术原理与工程应用剖析:从结构到性能优化

    在现代电子通信和射频领域,BNC接头作为一种广泛应用的连接器件,发挥着至关重要的作用。它以其可靠的连接性能、优异的电气特性和广泛的兼容性,成为众多电子设备和系统中不可或缺的一部分。本文将深入剖析
    的头像 发表于 12-31 16:07 2235次阅读
    BNC接头技术原理与<b class='flag-5'>工程</b>应用<b class='flag-5'>剖析</b>:从结构到性能优化