Protoc Buffer 是我们比较常用的序列化框架,Protocol Buffer 序列化后的占空间小,传输高效,可以在不同编程语言以及平台之间传输。今天这篇文章主要介绍 Protocol Buffer 使用 VarInt32 减少序列化后的数据大小。

VarInt32 编码

VarInt32 (vary int 32),即:长度可变的 32 为整型类型。一般来说,int 类型的长度固定为 32 字节。但 VarInt32 类型的数据长度是不固定的,VarInt32 中每个字节的最高位有特殊的含义。如果最高位为 1 代表下一个字节也是该数字的一部分。因此,表示一个整型数字最少用 1 个字节,最多用 5 个字节表示。如果某个系统中大部分数字需要 >= 4 字节才能表示,那其实并不适合用 VarInt32 来编码。下面以一个例子解释 VarInt32 的编码方式:

以 129 为例,它的二进制为 1000 0001 。由于每个字节最高位用于特殊标记,因此只能有 7 位存储数据。第一个字节存储最后 7 位 (000 0001),但并没有存下所有的比特,因此最高位置位 1,剩下的部分用后续字节表示。所以,第一个字节为:1000 0001第二个字节只存储一个比特位即可,因此最高位为 0 ,所以,第二个字节为:0000 0001这样,我们就不必用 4 字节的整型存储 129 ,可以节省存储空间

在 Protoc buffer 中,每一个 ProtoBuf 对象都有一个方法 public void writeDelimitedTo(final OutputStream output),该方法将 ProtoBuf 对象序列化后的长度以及序列化数据本身写入到输出流 output 中。多个对象调用该方法可以将序列化后的数据写入到同一个输出流。由于每次写入都有长度,所以反序列化时先解析长度,在读取对应长度的字节数据,即可解析出每个对象。该方法中对序列化后长度的编码便使用 VarInt32,因为一个 Protobuf 对象序列化后的长度不会太大,因此使用 VarInt32 编码能够有效的节省存储空间。接下来我们看下 Protoc Buffer 中如何实现 VarInt32 编码,跟进 writeDelimitedTo 方法,可以看到 VarInt32 编码的源码如下:

  /*** Encode and write a varint.  {@code value} is treated as* unsigned, so it won't be sign-extended if negative.*/public void writeRawVarint32(int value) throws IOException {while (true) {if ((value & ~0x7F) == 0) {//代表只有低7位有值,因此只需1个字节即可完成编码
        writeRawByte(value);return;} else {writeRawByte((value & 0x7F) | 0x80);//代表编码不止一个字节,value & 0x7f 只取低 7 位,与 0x80 进行按位或(|)运算为了将最高位置位 1 ,代表后续字节也是改数字的一部分
        value >>>= 7;}}}

该方法对 int 类型的值进行 VarInt32 编码,可以验证最多 5 个字节即可完成编码。

VarInt32 解码

理解了编码后,解码就没什么可说的了。就是从输入字节流中,读取一个字节判断最高位,将真实数据位拼接成最终的数字即可。Hadoop RPC 中使用了 Protoc Buffer 作为数据序列化框架。其中,Hadoop 针对 writeDelimitedTo 方法实现了对 VarInt32 的解码。源码如下:

/*** Read a variable length integer in the same format that ProtoBufs encodes.* @param in the input stream to read from* @return the integer* @throws IOException if it is malformed or EOF.*/public static int readRawVarint32(DataInput in) throws IOException {byte tmp = in.readByte();if (tmp >= 0) {// tmp >= 0 代表最高位是 0 ,否则 tmp < 0 代表最高位是 1 ,需要继续往下读return tmp;}int result = tmp & 0x7f;if ((tmp = in.readByte()) >= 0) {result |= tmp << 7;} else {result |= (tmp & 0x7f) << 7;if ((tmp = in.readByte()) >= 0) {result |= tmp << 14;} else {result |= (tmp & 0x7f) << 14;if ((tmp = in.readByte()) >= 0) {result |= tmp << 21;} else {result |= (tmp & 0x7f) << 21;result |= (tmp = in.readByte()) << 28;if (tmp < 0) {//我们说 VarInt32 最多 5 个字节表示,当程序执行到这里,tmp < 0,说明,编码格式有问题// Discard upper 32 bits.for (int i = 0; i < 5; i++) {if (in.readByte() >= 0) {return result;}}throw new IOException("Malformed varint");}}}}return result;}

在 Hadoop 源码中并没有使用循环去解码,而是使用多个 if 条件判断,根据 tmp 的正负号来判断最高位是否是 1。如果读取的该数字用了 5 个字节编码,当读到了第 5 个字节,理论上 tmp 应该大于 0 。但是如果 tmp 小于 0 ,说明编码格式有问题。在 Hadoop 源码中程序会继续往下读,最多再向下读 5 个字节且丢掉最高位仍然 < 0 的字节。如果在该过程某个字节最高位为 0 ,便停止读取直接返回。这个处理逻辑在其他框架源码中也有出现。

看完 Hadoop 的源码,我们在看看 Protoc Buffer 自己提供的解析源码:

  /*** Like {@link #readRawVarint32(InputStream)}, but expects that the caller* has already read one byte.  This allows the caller to determine if EOF* has been reached before attempting to read.*/public static int readRawVarint32(final int firstByte, final InputStream input) throws IOException {if ((firstByte & 0x80) == 0) {return firstByte;}int result = firstByte & 0x7f;int offset = 7;for (; offset < 32; offset += 7) {final int b = input.read();if (b == -1) {throw InvalidProtocolBufferException.truncatedMessage();}result |= (b & 0x7f) << offset;if ((b & 0x80) == 0) {return result;}}// Keep reading up to 64 bits.for (; offset < 64; offset += 7) {final int b = input.read();if (b == -1) {throw InvalidProtocolBufferException.truncatedMessage();}if ((b & 0x80) == 0) {return result;}}throw InvalidProtocolBufferException.malformedVarint();}

可以看到 Protoc Buffer 自己提供的解码方式与 Hadoop 是一样的,包括遇到错误的编码时候的异常处理方式也是一样的。

小结

本篇文章主要介绍了 VarInt32 编解码,VarInt32 表示一个整型数字最少用 1 个字节, 最多用 5 个字节。所以在传输数字大部分都比较小的场景下适合使用。当然,我们也可以用 VarInt64 来表示长整型的数字。 在介绍 VarInt32 的同时我们也看到了 ProtoBuf 和 Hadoop 这样的框架在传输数据的优化上不放过任何一个细节,值得我们学习。

公众号「渡码」

转载于:https://www.cnblogs.com/duma/p/11111427.html

Protoc Buffer 优化传输大小的一个细节相关推荐

  1. 还是关于前端性能优化,真的是的从细节开始

    前端性能优化(一) 前端是庞大的,包括 HTML. CSS. Javascript.Image .Flash等等各种各样的资源.前端优化是复杂的,针对方方面面的资源都有不同的方式.那么,前端优化的目的 ...

  2. 优化传输文件的性能- -零拷贝

    优化传输文件的性能- -零拷贝 文章目录 优化传输文件的性能- -零拷贝 一.为什么要有 DMA 技术? 二.传统的文件传输有多糟糕? 三.如何优化文件传输的性能? 四.如何实现零拷贝? mmap + ...

  3. 弱网络环境下最优调度和优化传输层协议方案

    一.背景 与有线网络通信相比,无线网络通信受环境影响比较大(例如高层建筑.用户移动.环境噪音.相对封闭环境等等),网络的服务质量相对来说不是非常稳定,导致用户经常会在弱信号的网络环境下通信.而当用户在 ...

  4. websocket传输数据大小限制_websocket设置传输大小

    tomcat中有提到: If the application does not define a MessageHandler.Partial for incoming text messages, ...

  5. 关于Linux LOOPBACK网口抓包的一个细节

    这个问题其实是我几个月前碰到,只是那时好像还在回忆着什么,心系上海,还没有完全适应这个新环境,加上这个问题也不是什么太深奥的问题,觉得太简单了,就搁置了.今天周末闲来无事就顺便写来来了.加上深圳经常下 ...

  6. 大小限制_只需一步!突破微信传输大小限制,轻松分享视频文件!

    如果问, 大家都用什么传输文件? 邮箱?U盘?QQ? 相信绝大部分的人都会说:微信. 近几年来, 随着微信的飞速发展, 大家已经普遍习惯使用微信来互传资料. 对于普通文件来说, 100MB的文件大小限 ...

  7. ProtoBuf(protocol buffer) 网络传输协议

    ProtoBuf(protocol buffer) 网络传输协议 转自:http://blog.csdn.net/wenyiqingnianiii/article/details/52741312 P ...

  8. 面试了一个32岁的程序员,一个细节就看出来是培训班的····

    首先,我说一句:培训出来的,优秀学员大有人在,我不希望因为带着培训的标签而无法达到用人单位和候选人的双向匹配,是非常遗憾的事情. 最近,在网上看到这样一个留言,引发了程序员这个圈子不少的轰动. &qu ...

  9. 【山东日照seo招聘】网站SEO优化中注意哪些小细节?

    [山东日照seo招聘]网站SEO优化中注意哪些小细节? 1.网页标题要合理     网页标题的重用占SEO优化过程中的40%,如果网页标题没有做好,那么在后期的优化过程中,就会事倍功半,网页标题主要从 ...

最新文章

  1. Kupu,“document-centric”的开放源码的客户端文本编辑器
  2. 6.23 NIUDAY 深圳站 | 从新零售到金融,Follow 技术大咖一起探索人工智能应用的无限可能...
  3. scrapy爬取动态网页_scrapy_splash 设置随机请求头
  4. Java输出箭头_java基于XSSF 导出带箭头的直线
  5. 基于git命令的代码统计方法
  6. 桌面的计算机被删掉了怎么调出来,误删了电脑桌面图标怎么办——一波超简单的操作,分分钟搞定它...
  7. c语言10个裁判,5个裁判可以对10个歌手进行打分,计算各个歌手的最终得分排列...
  8. Android 模拟器连接异常:Unable to connect to ADB server
  9. 【工具使用系列】关于 MATLAB Embedded Coder, 你需要知道的事
  10. 设计模式--代理模式Proxy(结构型)
  11. 拓端tecdat|R语言结构方程模型 SEM 多元回归和模型诊断分析学生测试成绩数据与可视化
  12. java中类初始化_java中类和对象的初始化
  13. data.getData()返回的Uri
  14. Python数据分析(4):jieba分词详解
  15. uniapp使用网易易盾
  16. 如何用油猴提升前端开发效率
  17. Arduino:数码管显示原理详解和Proteus仿真实验
  18. 基于局部自适应色差阈值的彩色图像边缘检测_爱学术
  19. Unity 2d 机器人的来回巡游
  20. Tesseract图文识别--简单

热门文章

  1. python list合并_python中如何合并两个列表
  2. oracle 24365,深入解析:DBA_OBJECTS中的OBJECT_ID与DATA_OBJECT_ID的区别
  3. windows下用elasticdump导入json数据到Elasticsearch中
  4. C++中的sta::atomic<bool>和auto类型
  5. 机器学习(MACHINE LEARNING) 【周志华版-”西瓜书“-笔记】 DAY9-聚类
  6. Android之Pull解析XML
  7. vue hot true 不起作用_从源码解读 Vuex 注入 Vue 生命周期的过程
  8. stm32 micropython vscode_在vscode里基于Pymakr插件进行esp32的micropython开发
  9. 风电功率预测matlab,一种基于二十四节气的风电功率预测方法与流程
  10. 网站优化有什么值得注意的小细节吗?