protobuf原理

285 阅读3分钟

ZigZag算法

ZigZag好处就是进行负数变换,原本负数首位为1导致的Varint编码无效,利用ZigZag技术就可以进行编码了

源码

第一位的1和0代表负和正
[+8] = [00001000]原
[-8] = [10001000]原

反码

第一位为0不变,第一位为负的话,他的其余位全部取反
[+8] = [00001000]原 = [0000 1000]反
[-8] = [10001000]原 = [1111 0111]反

补码

第一位为正不进行补偿,第一位为负最后一位加1
[+8] = [00001000]原 = [0000 1000]补
[-8] = [10001000]原 = [1111 0111]反 = [1111 1000]补

ZigZag

正数 0000 1000

  1. 数据左移一位: 0001 0000
  2. 符号位(正数的符号为0)放到最后一位:0001 0000

负数 1111 1000

  1. 左移一位:1111 0000
  2. 符号位放到最后一位: 1111 0001
  3. 除最后一位外全部取反:0000 1111

Varint编码

Varint编码后的数字,每次进行解码的时候就可以没8位的首位进行判定该字节是否到头,然后就可以紧凑的编码了

十进制300, 二进 00|00 0001 0|010 1100

  1. 从右到左没7位截取,后面还是该数字的一部分就填充1,到这里终止了就填充0,然后从左往右排
  2. 右边7位 = 010 1100 = 1010 1100
  3. 左边7位 = 00 0001 0 = 10
  4. 最终 10 1010 1100

Message Buffer

  1. keyvalue|keyvalue 紧凑型排列
  2. key定义 (field_number << 3) | wire_type
  3. field_number就是.proto中属性的数字,wire_type就是类型如下图

proto实现原理

在执行protoc编译的时候,命令行是这样写的:protoc -go_out=./ *.proto,Protobuf原生支持go所以认识-go_out,这表示把当前目录下所有proto都编译成go。如果我们命令行这样写:protoc -xxx_out=./ *.proto,由于不认识xxx,于是protoc会在PATH路径下寻找一个叫做protoc-gen-xxx的可执行文件。而protoc-gen-xxx就是我们要实现的插件

对比go的插件实现

  1. github.com/golang/prot…
  2. github.com/gogo/protob…

代码都是生成的,只是两种形式,一种是用generator的plugin来进行调用,一种是直接调用grpc来生成代码,但是都要使用generator来生成protobuf,因为要生成基本的数据结构,后续的grpc自行实现

生成的命令

protoc --netrpc_out=plugins=netrpc:. hello.proto

假如你生成的插件叫netrpc,那你的.exe文件就要叫做protoc-gen-netrpc, 这样protoc就会调用你的插件进行生成代码了

个人思考

代码生成本质就是使用一定的信息,根据模板生成固定的信息,像存前端使用前端的模板技术通过网页和js直接生成grpc和protobuf,像我经常就用layui的模板生成crud的代码,到这里说实话protobuf的字节码的编码和解码技术还是可以的,生成代码没啥东西