网易首页 > 网易号 > 正文 申请入驻

protocol buffer的高效编码方式

0
分享至

简介

protocol buffer这种优秀的编码方式,究竟底层是怎么工作的呢?为什么它可以实现高效快速的数据传输呢?这一切都要从它的编码方式说起。

定义一个简单的message

我们知道protocol buffer的主体就是message,接下来我们从一个简单的message出发,详细讲解protobuf中的编码方式。

比如下面的一个非常简单的消息对象:

message Student {
optional int32 age = 1;
}

在上面的例子中,我们定义了一个Student消息对象,并给他定义了一个名叫age的字段,并给它设置一个值叫做22。然后使用protobuf将其进行序列化,这么大的一个对象,对其序列化之后的字节如下所示:

08 96 00

很简单,使用三个字节就可以表示一个messag对象,数据量非常小。

那么这三个字节到底表示什么意思呢?一起来看看吧 。

Base 128 Varints

在解释上面的三个字节的含义之前,我们需要了解一个varints的概念。

什么叫Varints呢?就是序列化整数的时候,占用的空间大小是不一样的,小的整数占用的空间小,大的整数占用的空间大,这样不用固定一个具体的长度,可以减少数据的长度,但是会带来解析的复杂度。

那么怎么知道这个数据到底需要几个byte呢?在protobuf中,每个byte的最高位是一个判断位,如果这个位被置位1,则表示后面一个byte和该byte是一起的,表示同一个数,如果这个位被置位0,则表示后面一个byte和该byte没有关系,数据到这个byte就结束了。

举个例子,一个byte是8位,如果表示的是整数1,那么可以用下面的byte来表示:

0000 0001

如果一个byte装不下的整数,那么就需要使用多个byte来进行连接操作,比如下面的数据表示的是300:

1010 1100 0000 0010

为什么是300呢?首先看第一个byte,它的首位是1,表示后面还有一个byte。再看第二个byte,它的首位是0,表示到此就结束了。我们把判断位去掉,变成下面的数字:

010 1100 000 0010

这时候还不能计算数据的值,因为在protobuf中,byte的位数是反过来的,所以我们需要把上面的两个byte交换一下位置:

000 0010 010 1100

也就是:

10 010 1100

=256 + 32 + 8 + 4 = 300

消息体的结构

从message的定义可以知道,protobuf中的消息体的结构是key=value的形式,其中的key就是message中定义的字段的整数值1,2,3,4等。而value就是真正对其设置的值。

当一个消息被编码之后,这些key和value会被连接在一起,组成一个byte stream。当要对其进行解析的时候,需要定位到key和value的具体长度,所以在key中需要包含两部分,第一个部分就是字段在proto文件中的值,第二个部分就是value部分占用的长度大小。

只有通过这两个部分的值结合起来,解析器才能够正确的对字段进行解析。

key的这种格式,被称为 wire types,有哪些 wire types呢?我们看一下:

类型 含义 使用场景 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum 1 64-bit fixed64, sfixed64, double 2 Length-delimited string, bytes, embedded messages, packed repeated fields 3 Start group groups (deprecated) 4 End group groups (deprecated) 5 32-bit fixed32, sfixed32, float

可以看到除了3,4两种类型之外,其他的类型可以分为三类,一类是固定长度的类型,如1,5,他们分别是64位和32位的数字。

第二类是0,表示Varint,这是一种可变类型,用来表示通用的数字类型,bool类型和枚举类型。第三类2,表示长度区分的类型,这种类型通常用来表示字符串,字节数字等。

所有的key都是一个varint类型,它的值是:(field_number << 3) | wire_type,也就是说key的最后三个位,用来存储wire类型。

上面我们例子中的key的值是08,用二进制表示:

000 1000

最后三位是0,表示是一个Varint类型,将08右移三位,得到1,表示key表示的字段是1这个字段,也就是age。

然后我们看下剩下的部分96 00,换成二进制是:

96 00 = 1001 0110 0000 0000

根据Varint的定义,第一位表示的是连接位,表示第二个字节的内容和第一个字节的内容是一起的。对于Varint来说,需要将低位的字节和高位的字节进行交换,如下:

1001 0110 0000 0000 去掉最高位的1 :
001 0110 0000 0000 交换低位字节和高位字节:
0000 0000 001 0110

上面的值是16 + 4 + 2 = 22

这样我们就得到了值为1的key,对应的value是22。

符号整数

我们知道有两种表示符号整数的方式,一种是标准的int类型:int32 和 int64,一种是带符号的int类型:sint32 和 sint64。

这两种类型的区别在于对应负整数的表示上。对于int32和int64来说,所有的负整数都是以十个字节来表示的,所以占用的空间会比较大,不适合用来表示负整数。

如果使用sint32 和 sint64,那么使用的编码方式是ZigZag,对于负整数来说更加有效。

ZigZag将带符号的整数和无符号的整数进行映射,对于每个n来说,将会使用下面的公式来编码:

(n << 1) ^ (n >> 31)

对于sint64来说就是:

(n << 1) ^ (n >> 64)

举个例子:

符号整数 编码结果 0 0 -1 1 1 2 -2 3 2147483647 4294967294 -2147483648 4294967295

# 字符串

字符串的wire类型是2,说明它的值是一个varint编码的长度。举个例子:

message Student {
optional string name = 2;
}

上我们给Student定义了第二个属性name,假如给name赋值 “testing” ,那么得到的编码是:

12 07 [74 65 73 74 69 6e 67]

中括号的编码就是”testing”的UTF8表示。

0x12 可以这样解析:

0x12
→ 0001 0010 (binary representation)
→ 00010 010 (regroup bits)
→ field_number = 2, wire_type = 2

0x12表示字段2的类型是2,后面跟着的07就表示后续byte字节的长度了。

嵌套的消息

消息中可以嵌套消息,我们看一个例子:

message Teacher {
optional Student s = 3;
}

假如我们把s的age字段设置为22,就和第一个例子一样,那么上面的编码就是:

1a 03 08 96 00

可以看到后面的三个字节和第一个例子是一样的。前面两个字节的判断方式和字符串是一值的,这样就不再多讲。

总结

好了,protobuf的基本编码规则和实现已经讲完了。听起来是不是很奇妙?

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
马英九怒了,反对妻子周美青声明,萧旭岑回应,幕后黑手浮出水面

马英九怒了,反对妻子周美青声明,萧旭岑回应,幕后黑手浮出水面

DS北风
2026-05-22 21:50:15
生育大局已定:不出意外的话,26年起中国人口将迎来3大明显变化

生育大局已定:不出意外的话,26年起中国人口将迎来3大明显变化

阿凫爱吐槽
2026-05-23 04:14:26
一夜9大重磅!曼联抢人,巴萨留拉什福德,罗德里要投奔穆帅?

一夜9大重磅!曼联抢人,巴萨留拉什福德,罗德里要投奔穆帅?

有态度的体育小白
2026-05-23 06:26:43
抢的时候多嚣张,220天后就多狼狈:荷兰87亿曾打水漂大臣哭晕

抢的时候多嚣张,220天后就多狼狈:荷兰87亿曾打水漂大臣哭晕

闻识
2026-05-22 17:52:07
真分手了鹿晗深夜发文,曝真实现状,其实关父早有暗示

真分手了鹿晗深夜发文,曝真实现状,其实关父早有暗示

小椰的奶奶
2026-05-22 14:24:44
大量户外mini露营车流入闲鱼!来自农夫山泉,全新30元拿走

大量户外mini露营车流入闲鱼!来自农夫山泉,全新30元拿走

闲搞机
2026-05-20 11:04:52
安徽省合肥市委原副书记路军被开除党籍和公职

安徽省合肥市委原副书记路军被开除党籍和公职

界面新闻
2026-05-22 20:41:54
丢人!赴华航班华人夫妇高空大闹致飞机备降!女游客机场打滚尖叫

丢人!赴华航班华人夫妇高空大闹致飞机备降!女游客机场打滚尖叫

小鹿姐姐情感说
2026-05-22 08:58:29
拒绝受辱!多国首脑扎堆访华,普京走后巴铁又来,美高官只能排队

拒绝受辱!多国首脑扎堆访华,普京走后巴铁又来,美高官只能排队

南风的温柔
2026-05-23 03:46:27
炸锅!基德下课引发连锁反应,多队趁火打劫,独行侠真要重建了

炸锅!基德下课引发连锁反应,多队趁火打劫,独行侠真要重建了

体育大朋说
2026-05-22 14:15:03
U17国足2-3日本 2人备受争议:这不就是人祸吗?我们已22年冠军荒

U17国足2-3日本 2人备受争议:这不就是人祸吗?我们已22年冠军荒

小徐讲八卦
2026-05-23 06:27:44
80年代家喻户晓的女演员,嫁给军委副主席儿子,离婚后混得如何?

80年代家喻户晓的女演员,嫁给军委副主席儿子,离婚后混得如何?

老范谈史
2026-05-22 21:42:27
小鹏GX一夜爆单,李想余承东都没想到!

小鹏GX一夜爆单,李想余承东都没想到!

趣味萌宠的日常
2026-05-23 04:09:03
“碳水脸”引争议,网友:令人不适的网络词!

“碳水脸”引争议,网友:令人不适的网络词!

科学大观园
2026-05-21 14:01:05
蒋介石唯独不敢动周恩来?如果动他:估计老蒋整个嫡系都得反!

蒋介石唯独不敢动周恩来?如果动他:估计老蒋整个嫡系都得反!

兴趣知识
2026-05-22 17:53:06
北京球迷看台痛哭!CBA骑士让许利民火出圈 媒体人:白花那么多钱

北京球迷看台痛哭!CBA骑士让许利民火出圈 媒体人:白花那么多钱

颜小白的篮球梦
2026-05-22 22:01:24
太子集团陈志幕后大佬曝光:系公安部陈年督办案头目

太子集团陈志幕后大佬曝光:系公安部陈年督办案头目

华庭讲美食
2026-05-23 00:54:13
CBA谁能夺冠?杨鸣道出了自己的答案!

CBA谁能夺冠?杨鸣道出了自己的答案!

体育哲人
2026-05-22 19:38:53
毛远新透露,毛主席最后的遗嘱应该只有9个字

毛远新透露,毛主席最后的遗嘱应该只有9个字

历史伟人录
2024-09-27 13:44:11
被“数字围墙”困住的新能源车主:外面换刹车油都出故障码,必须回4S店在线匹配

被“数字围墙”困住的新能源车主:外面换刹车油都出故障码,必须回4S店在线匹配

金融界
2026-05-22 12:08:12
2026-05-23 07:04:49
flydean程序那些事
flydean程序那些事
最通俗的解读,最深刻的干货!
356文章数 438关注度
往期回顾 全部

科技要闻

雷军:输给特斯拉不丢人

头条要闻

伊朗前总统内贾德被指是"最大内鬼" 其目前下落不明

头条要闻

伊朗前总统内贾德被指是"最大内鬼" 其目前下落不明

体育要闻

最糟糕裁判?他想要退役当市长

娱乐要闻

周也恋情曝光!对象身份不简单

财经要闻

富途拟被罚18.5亿元 老虎 长桥也回应了

汽车要闻

11万级直接上四驱 银河星耀7限时权益价9.88万起

态度原创

教育
本地
家居
手机
公开课

教育要闻

本科入党积极分子,考研上岸还需要重新申请吗?

本地新闻

用云锦的方式,打开江苏南京

家居要闻

低调传承 温润沉静

手机要闻

荣耀首款磁吸副屏功能揭秘:15米实时预览拍照 续航可连拍5小时

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版