这里重点说一下Tag。Tag用来标识字段,通过Tag能获知这段字节流是属于什么类型数据的,其定义为:Tag = (field_number << 3) | wire_type
。这样,解包时就可根据tag将value对应消息中的字段。
Tag占用一个字节的长度(如果标识号超过了16,则多占用一个字节的位置,原因是field_number左移了3位,编码方式为Varint&Zigzag,编码的时候一个字节不够用了)。
message ChannelDataAck { bytes uuid = 1; //这里的1 、2就是field_number uint32 result = 2; }Tag(字段标识号)在序列化和反序列化过程中非常重要。举一个应用中非常常见的例子,在需要对原有结构进行增减字段的时候,同样一个结构体定义,新版本代码中对其增加了一个字段,那当新版本代码序列化后给原有旧版本反序列化解析的时候,因为旧的没有那个新增的字段,所以在解析时只解析自己有的字段,没有的不进行解析,这样旧的代码依旧能从新字节流中解析出旧数据结构。那旧的数据结构的数据解析为新数据结构时,因为没有新字段的数据,解析为新数据时该字段置为默认值。这样就能保证兼容性,对协议升级较为友好。
可以看到通过T-L-V的数据存储方式,能够较好的解决字段不完全匹配时的如何解析的问题。
序列化 & 反序列化过程序列化过程如下:
判断每个字段是否有设置值,有值才进行编码.
根据字段标识号&数据类型将字段值通过不同的编码方式进行编码.
反序列化过程如下:
解析从输入流读入的二进制字节数据流.
将解析出来的数据按照指定的格式读取到C++、Rust等对应的结构类型中.
到这里,基本把Protocol Buffers的工作原理简单梳理了一遍,其他技术细节待以后再深究。