zoukankan      html  css  js  c++  java
  • google的protobuf协议研究,以及实现protobuf对php的支持化【一】

    protobuf协议研究

    • 什么是protobuf

    protocol buffer简称protobuf,是google开发的一种语言无关、平台无关、扩展性好的用于通信协议、数据存储的结构化数据串行化方法。其相关资料文档都可以在这找到https://developers.google.com/protocol-buffers

    附:目前常见的数据序列化方法效率对比见这https://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

    • protobuf优缺点

    优点:相对于xml,json等序列化协议,protobuf更加高效、灵活、简单,在时间解析和空间压缩方面比其他大多数协议高效很多;支持大数据量数据传输;可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。

    缺点:由于protobuf序列化后的是二进制字节的数据,相对于与xml,json来说,视觉上不是那么友好;目前支持仅支持c++,java,python三种语言

    • protobuf使用及消息结构

    首先定义一个.proto文件,定义你需要的串行化的数据格式,这里定义为Person.proto,表示一个人的信息;

    message Person {
      required string name = 1;///sdfsfsdffds
      required int32 id = 2;
      optional string email = 3;

      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
      repeated PhoneType pt = 7;
      message PhoneNumber1 {
        required string aaa = 1;
        optional uint64 bb =2;
      }
      repeated PhoneNumber phone = 4;
      repeated PhoneNumber1 phone1 = 6;
    }

    如你所见,protobuf文件看起来有类似于c++,什么int,enum,string;

    protobuf消息结构

    protobuf消息结构都已“message msgname”开头,其中msgname是你要定义的消息段名,这里为Persson;

    接下来一般每一行都是类似于 “修饰词 数据类型 数据名字 = 序号 [default=默认值]”,

    修饰词有required,optional,repeated三种,required表示该值是必须要传的,而且只能出现一个;optional该值可以有零个或一个,可以查询其存在与否;repeated该值相当于一个数组或有序列表,改值可为0个、1个、或多个,可以使用选项packed = true来进行高效的编码。

    数据类型16种,其中基本数据类型15种加上message嵌套共16种;15种数据格式如下;根据需要选择相应的数据类型

    数据名字就是合法的关键字就行了

    序号就是该字段在该消息中出现的顺序编号,可以从0~2^29-1次方之间(因为每个字段类型都由一个int32的key表示,其中类型字段占后3位,剩下2^29个编号,一般从1往下顺序开始,故最大可容纳2^29个字段)

    [default=默认值]表示可以规定默认取值,该属性只对optional修饰的字段有效,表示如果没使用此字段但是规定了默认值,那么序列化的时候就按默认值序列化

    • protobuf序列化原理

    每一个消息字段序列化可以归结为key:value的配对,key可以唯一确定该字段类型,序号;value可以确定其值

    按照 “修饰词 数据类型 数据名字 = 序号 [default=默认值]”格式定义来分析其key和value构成

    key的构成: 由一个int32的4字节码表示;其公式为:(序号 << 3) | 字段归一类型;由varint编码序列化;字段归一类型如下所示

    字段归一类型由3位表示,最多能表示8种类型,由上图知目前已使用6种类型(其中3,4已经在2.0以后弃用,其他不变):注意:在实际实现过程中,repeated fields字段并不是按照2算的,是按照其数据类型算的

     如optional int32 test = 1; 反序列化之后是:0x08=0000 1000

     如optional string test1 = 2; 反序列化之后是:0x12=0001 0010

    value的序列化:采用little endian的存储方式

     Base 128 Varints编码

    varint编码使用一个字节或多个字节来表示一个整数,是边长字节编码,数越小,所以字节越少;每个字节都是varint类型,除了最后一个字节因为最有一个字节有空位;每个字节的低7位来存数据,最高位来表示前后两个字节是否有关联,为1表示后面的字节的数据和当前字节数据属于一个整体;

    对于正数编码:如300,其二进制是100101100;其varint编码是1010 1100 0000 0010<= 010 1100 + 000 0010 <= 0010 1100 + 0000 0001 litter endian<= 1 + 0010 1100

    对于负数编码,统一采用10个字节表示(最多能表示10*7=70位)32位和64位在最后一个字节上表现有所不同,对于32位的负数最后一个字节采用0x01填充,64采用0x0f填充

    如-4,二进制补码为0xfffffffb;采用32位vartint编码为:0xfbffffffffffffffff01;采用64位编码为:0xfbffffffffffffffff0f

    综上:varint适合于正整数编码,对于负数编码浪费空间

    Signed Integers的ZigZag编码

    对于有符号整数,采用ZigZag编码;其编码转换如下图

    即是:正数的2倍,负数的绝对值2倍-1;按照可得其公式:对于32位(n <<1)^(n >>31),相应的解码公式是(n>>1)^(n&0x01);对于64位(n<<1)^(n>>63),相应的解码公式是(n>>1)^(n&0x01);

    在protobuf协议中,对sint32,sint64位先采用zigzag编码, 然后在使用varint编码得到

    相对int32,int64来説,负数能节省空间;

    固定字节编码(fixed编码自己取名)

    采用固定4个字节:fixed32,sfixed32,float

    采用固定8个字节:fixed64,sfixed64,double

    无符号整数usint编码

    sint32采用varint编码,所不同的是,对于32位采用5个字节最后一个字节使用0x0f,对于64位采用10个字节,最后一个自己使用0x01

    相对于纯int32来説,负数能节省空间;

    string,bytes编码(官方手册上把repeated,embeded message也归为这一类,个人在2.5上实现经过实践,不是这样)

    如repeated string test = 2;test被初始化为"testing";那么其序列化后的值是12 07 74 65 73 74 69 6e 67;其中前第一个字节就是key的编码;第二个字节是字符串长度的varint编码;后面7个字节就是字符串的ascii

    • protobuf使用实例及数据结构分析

    先去google开源项目上下载最新的protobuf编译器https://code.google.com/p/protobuf/downloads/list

    使用如下命令把自己的.proto文件编译生成对应的语言源文件

    protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

    IMPORT_PATH 指定要编译的.proto文件目录,默认当前路劲为指定路径;可以使用-I=IMPORT_PATH来代替--proto_path=IMPORT_PATH;由于package指定的文件可以在多个地方,故--proto_path可以使用多次来指定多个目录;

    生成的源文件有3种选择--cpp_out表示生成c++源文件;--java_out表示生成java源文件;--python_out表示生成python源文件;后面跟着生成文件存放的目录

    最后一个参数表示要编译哪个.proto文件

  • 相关阅读:
    Java CountDownLatch应用
    servlet 表单
    servlet简单方法
    MySQL WHERE
    JavaScript typeof
    JavaScript字符串
    jsp语法
    HTML链接
    2021.3.10
    2021.3.9
  • 原文地址:https://www.cnblogs.com/buptbill220/p/3467614.html
Copyright © 2011-2022 走看看