protobuf_protobuff - CSDN
精华内容
参与话题
  • Protobuf 详解

    万次阅读 2019-04-28 11:03:29
    Protobuf全称是Google Protocol Buffer,是一种高效轻便的结构化数据存储方式,可用于(数据)通信协议、数据存储等。 也可以理解为结构化数据的序列化方法,可简单类比为XML(这里主要是指在数据通信和数据存储这些...

    简介

    Protobuf全称是Google Protocol Buffer,是一种高效轻便的结构化数据存储方式,可用于(数据)通信协议、数据存储等。
    也可以理解为结构化数据的序列化方法,可简单类比为XML(这里主要是指在数据通信和数据存储这些应用场景中序列化方面的类比,但个人认为XML作为一种扩展标记语言和Protobuf还是有着本质区别的),其具有以下特点:

    • 语言无关,平台无关
      Protobuf支持Java, C++, Python等多种语言,支持多个平台。
    • 高效
      比XML更小(3~10倍),更快(20 ~ 100倍),更为简单。
    • 扩展性,兼容性好
      你可以更新数据结构,而不影响和破坏原有的旧程序。

    这里稍微解释一下序列化:
    **序列化:**将数据结构或对象转换成能够被存储和传输(例如网络传输)的格式,同时应当要保证这个序列化结果在之后(可能是另一个计算环境中)能够被重建回原来的数据结构或对象。

    Protobuf 使用

    • 创建.proto文件,定义数据结构
      例如:
    //例:在xxx.proto文件中定义Example1 message
    message Example1{
    	optional string stringVal =1;
    	optional bytes bytesVal =2;
    	message EmbeddedMessage{
    		int32 int32Val = 1;
    		string stringVal = 2;
    	}
    	optional EmbededMessage embeddedExample = 3;
    	repeated int32 repeatedInt32Val = 4;
    	repeated string repeatedStringVal = 5;	
    }
    

    我们在上例中定义了一个名为 Example1 的 消息,语法很简单,message 关键字后跟上消息名称:

    message xxx{
    }
    

    之后我们在其中定义了message具有的字段,形式为:

    message xxx {
      // 字段规则:required -> 字段只能也必须出现 1 次
      // 字段规则:optional -> 字段可出现 0 次或多次
      // 字段规则:repeated -> 字段可出现任意多次(包括 0)
      // 类型:int32、int64、sint32、sint64、string、32-bit ....
      // 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字)
      字段规则 类型 名称 = 字段编号;
    }
    

    在上例中,我们定义了:

    • 类型string,名为stringVal的optional可选字段,字段编号为1,此字段可出现0次或1次
    • 类型bytes,名为bytesVal的optional可选字段,字段编号为2,此字段可出现0次或1次
    • 类型 EmbeddedMessage(自定义的内嵌 message 类型),名为 embeddedExample1 的 optional 可选字段,字段编号为 3,此字段可出现 0 或 1 次
    • 类型 int32,名为 repeatedInt32Val 的 repeated 可重复字段,字段编号为 4,此字段可出现 任意多次(包括 0)
    • 类型 string,名为 repeatedStringVal 的 repeated 可重复字段,字段编号为 5,此字段可出现 任意多次(包括 0)

    关于proto2定义message消息的更多语法细节,例如具体支持哪些类型,字段编号分配,import导入定义,请参考文章最后的Reference。

    1. protoc编译.proto文件生成读写接口
      我们在.proto文件中定义了数据结构,这些数据结构是面向开发者和业务程序的,并不面向存储和传输。
      当需要把这些数据进行存储或传输时,就需要将这些结构数据进行序列化、反序列化以及读写。那么如何实现呢?不用担心,ProtoBuf 将会为我们提供相应的接口代码。如何提供?答案就是通过 protoc 这个编译器。
      可通过如下命令生成相应的接口代码:
    // $SRC_DIR: .proto 所在的源目录
    // --cpp_out: 生成 c++ 代码
    // $DST_DIR: 生成代码的目标目录
    // xxx.proto: 要针对哪个 proto 文件生成接口代码
    
    protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto
    

    最终生成的代码将提供类似如下的接口:

    Reference:
    https://www.jianshu.com/p/a24c88c0526a

    展开全文
  • Protobuf的简单介绍、使用和分析

    万次阅读 2016-03-06 16:57:05
    Protobuf的简单介绍、使用和分析 一、protobuf是什么?  protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间...

    Protobuf的简单介绍、使用和分析


    一、protobuf是什么?

            protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。后面将会有简单的demo对于这两种格式的数据转化效率的对比。但这个库目前使用还不是太流行,据说谷歌内部很多产品都有使用。

     

    二、protobuf有什么?

            Protobuf 提供了C++、java、python语言的支持,提供了windows(proto.exe)和linux平台动态编译生成proto文件对应的源文件。proto文件定义了协议数据中的实体结构(message ,field)

    关键字message: 代表了实体结构,由多个消息字段(field)组成。

    消息字段(field): 包括数据类型、字段名、字段规则、字段唯一标识、默认值

    数据类型:常见的原子类型都支持(在FieldDescriptor::kTypeToName中有定义)

    字段规则:(在FieldDescriptor::kLabelToName中定义)

            required:必须初始化字段,如果没有赋值,在数据序列化时会抛出异常

            optional:可选字段,可以不必初始化。

            repeated:数据可以重复(相当于java 中的Array或List)

            字段唯一标识:序列化和反序列化将会使用到。

    默认值:在定义消息字段时可以给出默认值。

     

    三、protobuf有什么用?

            Xml、Json是目前常用的数据交换格式,它们直接使用字段名称维护序列化后类实例中字段与数据之间的映射关系,一般用字符串的形式保存在序列化后的字节流中。消息和消息的定义相对独立,可读性较好。但序列化后的数据字节很大,序列化和反序列化的时间较长,数据传输效率不高。

            Protobuf和Xml、Json序列化的方式不同,采用了二进制字节的序列化方式,用字段索引和字段类型通过算法计算得到字段之前的关系映射,从而达到更高的时间效率和空间效率,特别适合对数据大小和传输速率比较敏感的场合使用。

    四、Protobuf在Android上的使用

    1、创建proto文件,定义消息的实体结构

    2、编译proto文件生成对应的java文件

    3、添加protobuf-java-2.5.0.jar到android工程

    4、在android中实现对消息结构的序列化/反序列化  

     

    五、Protobuf与json的对比

    1、创建product.proto文件

            定义了三个Message(ProductInfo、PhoneInfo、Watch)消息结构

    2、消息结构对应的java类(ProductInfo、PhoneInfo、Watch)

     

    3、消息结构和java对象赋值

    PhoneName:” idol3”

    Price:2000

    Top:1

     

    WatchName:” tcl watch”

    Price:1000

    Top:1

     

    4、JSON字符串

     

    {"phone":{"phoneName":"idol3","price":2000,"top":1},"watch":{"watchName":"tcl wtch","top":1,"price":1000}}

     

    5、Protobuf转化后的二进制文件

     

    空间效率

    Json:107个字节

    Protobuf:32个字节

     

    时间效率

    Json序列化: 1ms ,  反序列化:0ms

    Protobuf 序列化: 0ms 反序列化:0ms

     

    将public List<Phone> list和repeated PhoneInfo phoneInfoList =3;都赋值为1000个PhoneInfo

     

    空间效率

    Json:4206个字节

    Protobuf:1332个字节

     

    时间效率

    Json序列化: 4ms ,  反序列化:1ms

    Protobuf 序列化: 1ms 反序列化:0ms

    六、protobuf的简单分析

    1、优缺点

    优点:通过以上的时间效率和空间效率,可以看出protobuf的空间效率是JSON的2-5倍,时间效率要高,对于数据大小敏感,传输效率高的模块可以采用protobuf库

     

    缺点:消息结构可读性不高,序列化后的字节序列为二进制序列不能简单的分析有效性;目前使用不广泛,只支持java,C++和Python;

     

    2、数据序列化/反序列化

    a、规则:

    protobuf把消息结果message也是通过 key-value对来表示。只是其中的key是采取一定的算法计算出来的即通过每个message中每个字段(field index)和字段的数据类型进行运算得来的key = (index<<3)|type;

    type类型的对应关系如下:

     

    Type

    Meaning

    Used For

    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

     

    Value会根据数据类型的不同会有两种表现形式:

    对于各种int,bool,enum类型,value就是Varint

    对于string,bytes,message等等类型,value就是length+原始内容编码

     

    Varints是一种紧凑表示数字的方法。它用一个或者多个字节表示一个数字,值越小的数字字节数越少。相对于传统的用4字节表示int32类型数字,Varints对于小于128的数值都可以用一个字节表示,大于128的数值会用更多的字节来表示,对于很大的数据则需要用5个字节来表示。

     

    Varints算法描述: 每一个字节的最高位都是有特殊含义的,如果是1,则表示后续的字节也是该数字的一部分;如果是0,则结束

    b、demo生成的的二进制文件反序列化。

    第1个字节 (0A)

    字段索引(index):         0A = 0001010  0A>>3 = 001 = 1

    数据类型(type):           0A = 0001010&111  = 2 (String);

     

    第2个字节 (0C)

    字符串长度(length):      0E = 12;

    字符串:                         0A 05 69 64 6F 6C 33 10 01 18 BD 0F

     

    第3个字节 (0A)

    因为字符串是来自phoneInfo属于嵌套类型

    字段索引(index):         0A = 0001010  0A>>3 = 001 = 1

    数据类型(type):           0A = 0001010&111  = 2 (String);


    第4-9个字节(69 64 6F 6C 33)

    字符串长度(length):    05 = 5

    字符串:                       69 64 6F 6C 33 = idol3

     

    第10个字节 (10)

    字段索引(index):         10 = 00010000    10A>>3 = 0010 = 2

    数据类型(type):           10 = 00010000&111  = 0 (Varints);

     

    第11个字节  (01)

    Varints:                          01 = 00001字节的最高位为0 整数结束

    Value:                            1;

     

    第12个字节(18)

    字段索引(index):           18 = 00011000    18>> 00011 = 3

    数据类型(type):           18 = 00011000&111  = 0 (Varints);

     

    第13个字节(D0)

    最高位为1,整数计算到下一个字节

     

    第14个字节(0F)

    最高位为0,整数计算结束

    Value:为11111010000 =2000

     

    C、反序列化结果

    phoneinfo为

    phoneName = “idol3”

    top = 1

    price = 2000;

     

    同样的方法watchInfo为:

    watchName = “tcl name”

    top = 1

    price=2000 

    3、时间效率

    通过protobuf序列化/反序列化的过程可以得出:protobuf是通过算法生成二进制流,序列化与反序列化不需要解析相应的节点属性和多余的描述信息,所以序列化和反序列化时间效率较高。

    4、空间效率

    xml、json是用字段名称来确定类实例中字段之间的独立性,所以序列化后的数据多了很多描述信息,增加了序列化后的字节序列的容量。

     

    Protobuf的序列化/反序列化过程可以得出:

    protobuf是由字段索引(fieldIndex)与数据类型(type)计算(fieldIndex<<3|type)得出的key维护字段之间的映射且只占一个字节,所以相比json与xml文件,protobuf的序列化字节没有过多的key与描述符信息,所以占用空间要小很多。

    七、Protobuf的源码分析

    1、protobuf在java使用的序列化流程

     

    java程序调用parserFrom(byte[] data)开始字节序列的反序列,Java程序通过调用编译生类GenerateMessage中的wirteTo()方法开始将序列化后的字节写入输出流中

     

    GenerateMessage 继承AbstractMessage类,序列化最终在AbstractMesssage中完成,序列化的实现过程:

    a、遍历对象中Message结构()

    调用AbstractMessage类中的writeTo()方法

     

    b、 序列化Message中每一个字段

    调用CodeOutputStream类中的writeMessageSetExtension()方法

     

    c、 对于Varints  Tag 的序列化流程:

    调用CodeOutputStream类中的writeUInt32()方法

    调用CodeOutputStream类中的WriteRawVarint32()方法

     

    d、 对于非Varints Tag的序列化

    调用CodeOutputStream类中的WriteTag()方法

     

     

    具体的序列化实现都在CodedOutputStream中完成

     

    2、java使用protobuf 的反序列化流程分析

    java程序通过调用parserFrom(byte[] data)开始反序列化

     

    具体在com.google.protobuf. AbstractParser类中实现

     

     

     

     

     

    最后在com.google.protobuf.CodedInputStream类中完成反序列化

    3、动态编译

    以windows下用protoc.exe工具实现proto文件编译为例,protoc.exe是用C++实现。在控制台执行命令:

    编译的流程:

    检查proto的语法规则

    将proto的文件中的message结构转换为GenerateMessage类的子类,并实现Builder接口。

    编译流程

    Main.cc中的main()方法

     

    Command_line_interface.cc中的Run()方法

     

    Import类中Import()

     

    在Descriptor中完成message消息的收集和转化。

    展开全文
  • protobuf精讲

    2019-09-23 10:45:21
    主要讲解了google protocol buffer(以下简称protobuf)在windows平台和linux平台下的编译及使用。其中涉及跨平台编译管理工具cmake的安装及使用,protobuf的使用例子及原理,以及protobuf的高级使用方法等。
  • Protobuf的介绍

    万次阅读 2018-07-20 11:26:05
    参看:https://blog.csdn.net/carson_ho/article/details/70568606 参看:... 一、简介  Google Protocol Buffer(简称Protobuf)是Google公司内部的混合语言数据标准,用于RPC系统...

    参看:https://blog.csdn.net/carson_ho/article/details/70568606

    参看:https://developers.google.com/protocol-buffers/docs/proto3

    一、简介

            Google Protocol Buffer(简称Protobuf)是Google公司内部的混合语言数据标准,用于RPC系统和持续数据存储系统。是一种轻便高效的可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

    二、特点

    1、优点:

            (1)、性能好/效率高,序列化和反序列化的时间开销都很小。(注:参看《全方位评测:Protobuf性能到底有没有比JSON快5倍?》 http://www.52im.net/thread-772-1-1.html

            (2)、支持向后兼容和向前兼容,协议中增加新域不会影响依赖原协议的客户端。

            (3)、平台无关、语言无关、可扩展。

            (4)、支持多种编程语言,目前支持Java、C++、Python、Java Lite、Ruby、JavaScript、Object-C、C#、Go。

     2、 缺点:

            (1)、二进制格式导致可读性差,为了提高性能,protobuf采用二进制编码,可读性差。

            (2)、缺乏自描述,二进制的协议内容必须配合.proto文件的定义才有含义。

    三、历史及版本

            Protobuf最初是在Google开发的,用以解决索引服务器的请求、响应协议。

            Protobuf现在是Google公司内部的通用语言数据标准,已经在使用的有超过48162种报文格式定义和超过12182个.proto文件。它们广泛用于RPC系统或持续的数据存储系统 。

            由于Google在开源protobuf之前,已经在使用protobuf的第二个版本,所以开源时定的是proto2(从V2.0.0开始),目前的最新版本是proto3。

    四、语言规范

    (proto3, 参看:https://developers.google.com/protocol-buffers/docs/proto3)

    1、消息类型

    (1)、定义一个消息,例如:

    syntax = "proto3";
    
    message SearchRequest {
    	string query = 1;
    	int32 page_number = 2;
    	int32 result_per_page = 3;
    }

    (2)、第一行指定了是使用proto3语法,如果没有这一行声明,protobuf编译器将假定你使用proto2语法。如果声明这一行的话,该声明必须是.proto文件的第一行(排除空行、注释行)。

    (3)、消息类型使用message关键字声明。

    (4)、消息类型中可以引用其他消息类型,例如:

    message SearchResponse {
      repeated Result results = 1;
    }
    
    message Result {
      string url = 1;
      string title = 2;
      repeated string snippets = 3;
    }

    2、字段类型

    (1)、字段类型可以是普通基本类型,也可以是复杂类型(枚举、Map或其他消息类型);

    (2)、消息定义中的每个字段都有一个唯一的数字编号,这个编号在protobuf消息的二进制格式中标识域,所以一旦消息定义中使用,最好不要改动。另外,字段编号1到15使用一个字节编码(编号本身 + 域类型),编号16到2047使用两个字节,因此,应该将编号1到15分配给频繁使用的字段(记得为将来可能频繁使用的字段预留1到15之内的编号);字段编号最小可以指定为1,最大可以指定为2的29次方 减1(536870911),不能使用编号19000到19999(这些是protobuf实现使用的);

    (3)、 指定字段的限定符,在proto2中,required前缀表示该字段为必要字段(即在序列化和反序列化之前该字段已经被赋值),optional前缀标识可选字段。在proto3中,singular前缀标识该字段是可选或必要字段(该字段不能多于一个)。repeated前缀标识可重复字段,这样的字段可以重复出现多次(也可以是零次),重复值得次序是会被保存的。

    (4)、保留字段(Reserved Fields)

            如果需要修改消息类型,比如删除一个字段、注释掉一个字段,后续的使用者如果重新使用这个字段编号,就会导致旧版本解析异常。可以将这些删除的字段的编号设置为reserved,这样如果后续有人使用这些字段编号,protobuf编译器将报错。标记方法示例如下:

    message Foo {
      reserved 2, 15, 9 to 11;
      reserved "foo", "bar";
    }

    注:上述标记标识字段编号2、15、9~11是保留的,不能被使用;字段名称”foo“、”bar“也是保留的。

            不能在一条reservced语句中,混合使用字段名和字段编号。  

            reservced 字段名,在JSON 序列化时会引起问题。   

    (5)、Protobuf基本类型与其他语言类型的对照表

    .proto Type

    Notes

    C++ Type

    Java Type

    double

     

     double

     double

    float

     

     float

     float

    int32

    Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.

     int32

     int

    int64

    Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.

     int64

     long

    uint32

    Uses variable-length encoding.

     uint32

     int

    uint64

    Uses variable-length encoding.

     uint64

     long

    sint32

    Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.

     int32

     int

    sint64

    Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. 

     int64

     long

    fixed32

    Always four bytes. More efficient than uint32 if values are often greater than 228. 

     uint32

     int

    fixed64

    Always eight bytes. More efficient than uint64 if values are often greater than 256.

     uint64

     long

    sfixed32

    Always four bytes.

     int32

     int

    sfixed64

    Always eight bytes.

     int64

     long

    bool

     

     bool

     boolean

    string

    A string must always contain UTF-8 encoded or 7-bit ASCII text.

     string

     String

    bytes

    May contain any arbitrary sequence of bytes.

    string

    ByteString

    (6)、默认值

            如果待解析的消息中没有包含singular字段元素,解析时会被设置为默认值。

            * 对于string,默认值为空字符串;

            * 对于bytes,默认值为空bytes;

            * 对于bools,默认值为false;

            * 对于数字类型,默认值为0;

            * 对于枚举类型,默认值是第一个枚举元素(该元素标号必须为0);

            * 对于消息域类型,不同的语言默认值不同。

            * 对于repeated类型的字段,默认值为空,不同语言里一般对应空列表。

    (7)、枚举类型

            可以定义枚举类型的消息类型,也可以在消息中定义枚举类型的字段,例如:

    message SearchRequest {
      string query = 1;
      int32 page_number = 2;
      int32 result_per_page = 3;
      enum Corpus {
        UNIVERSAL = 0;
        WEB = 1;
        IMAGES = 2;
        LOCAL = 3;
        NEWS = 4;
        PRODUCTS = 5;
        VIDEO = 6;
      }
      Corpus corpus = 4;
    }

    注:* 枚举通常应该从0值开始,因为我们可以使用0作为默认值,也为了兼容proto2(第一个值为默认值)。

           * 枚举常量值必须在32位int值范围之内,负数的常量值是无效的。

           * 枚举可以定义在消息内部,也可以定义在外部(被用作消息类型),也可以在消息类型中引用其他消息类型中的定义的枚举,如:MessageType.EnumType。

    可以定义不同的枚举常量使用相同的数字值,但需要设置allow_alias选项为true,不设置的话,会导致编译器报错。示例:

    enum EnumAllowingAlias {
      option allow_alias = true;
      UNKNOWN = 0;
      STARTED = 1;
      RUNNING = 1;
    }
    enum EnumNotAllowingAlias {
      UNKNOWN = 0;
      STARTED = 1;
      // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
    }

    (8)、在proto3中使用proto2消息类型定义

            导入proto2的消息类型,并在proto3的消息类型中使用也是可行的。但是proto2的枚举不能直接在proto3语法下使用,在导入的proto2消息类型中使用是没问题的。

    (9)、消息类型嵌套定义

            可以在一个消息类型中嵌套定义其他的消息类型。可以嵌套定义多层。

    (10)、Any类型

            Any类型,允许在不定义消息类型的情况下以内嵌的方式使用消息。每一个Any类型都可以包含任意的可序列化的消息的二进制描述,通过为它们分配唯一的URL来区分类型,使用Any类型,需要导入google/protobuf/any.proto。例如:

    import "google/protobuf/any.proto";
    
    message ErrorStatus {
      string message = 1;
      repeated google.protobuf.Any details = 2;
    }

            Any类型默认的URL类型为type.googleapis.com/packagename.messagename.

    不同的语言支持pack和unpack Any类型的值,在Java中,有pack()和unpack()方法,在C++中有PackFrom()和UnpackTo()方法,解析示例如下:

    // Storing an arbitrary message type in Any.
    NetworkErrorDetails details = ...;
    ErrorStatus status;
    status.add_details()->PackFrom(details);
    
    // Reading an arbitrary message from Any.
    ErrorStatus status = ...;
    for (const Any& detail : status.details()) {
      if (detail.Is<NetworkErrorDetails>()) {
        NetworkErrorDetails network_error;
        detail.UnpackTo(&network_error);
        ... processing network_error ...
      }
    }

    (11)、Oneof类型

            如果在一个消息中,如果多个字段在某个时刻只有一个会被用到,使用oneof可以强制这种行为。同一时间只能设置oneof中的一个字段,一个字段被设置后自动清理oneof中的其他字段。

            注意:oneof字段不能被限定为repeated的。

    示例:

    message SampleMessage {
      oneof test_oneof {
        string name = 4;
        SubMessage sub_message = 9;
      }
    }

    (12)、Maps

            protobuf中声明map的语法为:map<key_type, value_type> map_field = N;

            key_type可以是整数或字符串类型,注意枚举类型是不能作为key_type的,value_map可以是除map之外的任意类型。

    注:Map字段不能限定为repeated;

            map中值存储的顺序是未定义的,不能依赖这个顺序;

    Protobuf中maps的替代方案是:

    message MapFieldEntry {
      key_type key = 1;
      value_type value = 2;
    }
    
    repeated MapFieldEntry map_field = N;

    3、.proto文件

    (1)、多个消息类型可以定义在一个.proto文件中,通常相互关联的消息定义在一个文件中。

    (2)、在文件中增加注释,采用C/C++风格,使用“//” 或者“/* ... */”语法。例如:

     

    (3)、不同语言根据.proto文件生成的代码不同:

    对C++来说,编译器会为每个.proto文件生成一个.h和.cc文件,其中每个消息类型对应一个类定义;

    对Java来说,编译器会为每个.proto文件中的消息类型生成一个类(可能是一个.java文件或多个.java文件,取决于.proto的options),同时也会为每个消息类生成特定的构建器类;

    对于C#来说,编译器为每个.proto文件生成一个.cs文件,其中每个消息类型对应一个类定义。

    (4)、导入定义

            通过在一个.proto文件中导入其他的.proto文件,可以使用其他文件中的定义,导入方式如下所示:

    import "myproject/other_protos.proto";

            默认情况下,只能使用直接导入的定义,如果要使用间接导入的定义,需要使用import public声明,例如:

    // new.proto
    // All definitions are moved here
    
    // old.proto
    // This is the proto that all clients are importing.
    import public "new.proto";
    import "other.proto";
    
    // client.proto
    import "old.proto";
    // You use definitions from old.proto and new.proto, but not other.proto

    注:protobuf编译器生成代码时会根据编译器命令行 -I/--proto_path指定的一系列路径去搜索导入文件,如果没有指定-I/--proto_path标记,将会搜索编译器调用的.proto文件指定的导入文件。通常应该设置--proto_path为项目的跟路径并且使用所有导入文件的全限定名来指定导入文件。

    (5)、Packages

            可以在.proto文件中使用package声明,来避免消息类型的名字冲突。例如:

    package foo.bar;
    message Open { ... }

    可以以如下方式使用:

    message Foo {
      ...
      foo.bar.Open open = 1;
      ...
    }

    在C++中,Package声明限定,生成代码后会成为命名空间,比如:foo::bar;

    在Java中,Package声明限定,生成代码后会成为Java包名,除非额外使用option java_package来指定java包名。

    (6)定义服务

            如果想在一个RPC系统中使用消息类型,可以在.prpto文件中定义一个RPC服务接口。protobuf编译器将生成服务接口代码和stubs。定义示例如下:

    service SearchService {
      rpc Search (SearchRequest) returns (SearchResponse);
    }

            使用protobuf最简单地RPC系统是gRPC,一个Google开发的语言无关,平台无关的开源RPC系统,不使用gRPC,也可以选择其他的RPC实现。

    (7)、JSON映射

            Proto3支持标准的Json编码,方便系统之间通过json共享数据。如果Json编码的元素缺失或者值为null,解析后将会为protobuf的默认值,如果域使用默认值,编码为Json时可能省略该元素保存空格。通常可以通过一些选项控制Json编码格式的输出细节。

    来源: https://developers.google.com/protocol-buffers/docs/proto3#oneof-features

    proto3 JSON JSON example Notes
    message object {"fooBar": v, "g": null,…} Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_nameoption) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type.
    enum string "FOO_BAR" The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values.
    map<K,V> object {"k": v, …} All keys are converted to strings.
    repeated V array [v, …] null is accepted as the empty list [].
    bool true, false true, false  
    string string "Hello World!"  
    bytes base64 string "YWJjMTIzIT8kKiYoKSctPUB+" JSON value will be the data encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with/without paddings are accepted.
    int32, fixed32, uint32 number 1, -10, 0 JSON value will be a decimal number. Either numbers or strings are accepted.
    int64, fixed64, uint64 string "1", "-10" JSON value will be a decimal string. Either numbers or strings are accepted.
    float, double number 1.1, -10.0, 0, "NaN","Infinity" JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity". Either numbers or strings are accepted. Exponent notation is also accepted.
    Any object {"@type": "url", "f": v, … } If the Any contains a value that has a special JSON mapping, it will be converted as follows: {"@type": xxx, "value": yyy}. Otherwise, the value will be converted into a JSON object, and the "@type" field will be inserted to indicate the actual data type.
    Timestamp string "1972-01-01T10:00:20.021Z" Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than "Z" are also accepted.
    Duration string "1.000340012s", "1s" Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix "s". Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix "s" is required.
    Struct object { … } Any JSON object. See struct.proto.
    Wrapper types various types 2, "2", "foo", true,"true", null, 0, … Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer.
    FieldMask string "f.fooBar,h" See fieldmask.proto.
    ListValue array [foo, bar, …]  
    Value value   Any JSON value
    NullValue null   JSON null

            Json输出的控制选项:

            * 在proto3中,默认情况下JSON输出会忽略保存默认值的字段,可以通过选项改变这种行为,使JSON输出保存默认值;

            * 在proto3中,JSON解析器默认会丢弃未知字段,可以通过选项控制在解析时忽略未知字段;

            * 在proto3中,默认JSON输出会转换域名为驼峰式地名字(形同Java驼峰变量名形式),可以选项控制按字段名输出。

            * 枚举值得默认JSON输出为字符串枚举值名称,可以选项控制输出为数字值。

    (8)、选项声明

            java_package(file option) : 指定生成的Java类的包名;例如:option  java_package = "com.example.foo";

            java_multiple_files(file option): 将.proto文件中的消息类型定义成顶层的class,而不是默认的内部类。例如:option java_multiple_files = true;

            java_outer_classname(file option):指定.proto生成的最外层类的名称,默认是.proto文件名(转换为驼峰式后的样子)。例如:option java_outer_classname = "Ponycopter";

            optimize_for(file option) : 可以设置SPEED、CODE_SIZE或LITE_RUNTIME,默认会使SPEED的,这时protobuf编译器生成的代码将是性能最优化的,CODE_SIZE,编译器将生成代码量最小但性能稍差的代码,多用于包含大量.proto文件并且不需要全部都快速执行。LITE_RUNTIME:编译器仅生成依赖“lite”运行库的代码版本,通常对于受限平台(比如手机),仅生成少量的SPEED模式的API.

    例如:option  optimize_for = CODE_SIZE;

            cc_enable_arenas(file option) : 为C++生成的代码开启空间分配;

            objc_class_prefix(file option) : 为Objective-C类添加前缀;

            deprecated(field option):设置为true,表示字段是废弃的,不能在新代码中使用,可以考虑使用reserved语句替代。

    例如:int32 old_field = 6 [deprecated=true];

    注:Protocol Buffers允许自定义选项。

    (9)、代码生成

    protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto

    4、兼容性

    (1)、扩展消息类型

    要遵循一下规则:

            * 不要改变已存在的域的字段编号;

            * 增加一下新字段,旧版本序列化的消息可以通过新版本生成的代码解析,新版本序列化的消息也可以通过旧版本生成的代码解析(新增加的字段会被 当做未知字段)

            * 当一个字段在你要更新的消息类型中不再使用时,可以将该域移除,需要修改限定符为reserved;将一个域重命名,需要添加前缀“OBSOLETE_”;

            * int32、uint32、int64、uint64、bool是兼容的,可以将字段类型从这些类型中的一个改变为另一个,这样做不会破坏向前向后兼容性,但要注意类型的截取;

            * sint32、sint64是兼容的,但是跟其他的整型不兼容。fixed32域sfixed32兼容,fixed64域sfixed64兼容;

            * enum类型与int32、uint32、int64、uint64兼容(注意值可能被截取)。但是注意不同语言在消息反序列化时的不同处理(比如,不在范围内的枚举常量值,能被保存到消息中,但是在消息解析时不同语言怎么呈现是不一样的);

            * 改变一个单值为一个新的oneof限定的类型的一个字段,是安全的并且二进制兼容,移动多个字段到一个oneof类型中,只要这几个字段不会再同时使用就是安全的。但是将一个字段移动到一个已存在的oneof类型中是不安全的。

     

    五、命名规范

    1、消息名与字段名

    (1)、消息名采用大写开头的驼峰命名形式,例如:SongServerRequest

    (2)、字段名采用下划线分离的小写形式,例如song_name

    例如:

    message SongServerRequest {
      required string song_name = 1;
    }

    使用这样的命名规范,生成的代码看起来如下:

    C++:
      const string& song_name() { ... }
      void set_song_name(const string& x) { ... }
    
    Java:
      public String getSongName() { ... }
      public Builder setSongName(String v) { ... }

    2、枚举名

            枚举名使用大写开头的驼峰命名形式,枚举值采用下划线分隔的大写形式,例如

    enum Foo {
      FIRST_VALUE = 0;
      SECOND_VALUE = 1;
    }

    3、服务名

            RPC服务接口,服务名、RPC接口名均采用大写开头的驼峰命名形式。例如:

    service FooService {
      rpc GetSomething(FooRequest) returns (FooResponse);
    }

    参看:https://blog.csdn.net/u010019717/article/details/44830573

    展开全文
  • Google 的开源技术protobuf 简介与例子

    万次阅读 多人点赞 2017-09-12 14:49:48
    本文来自CSDN博客:http://blog.csdn.net/program_think/archive/2009/05/31/4229773.aspx 今天来介绍一下“Protocol Buffers”(以下简称protobuf)这个玩意儿。本来俺在构思“生产者/消费者模式 ”系列的下一个...

     

     

     

     

    今天来介绍一下“Protocol Buffers”(以下简称protobuf)这个玩意儿。本来俺在构思“生产者/消费者模式 ”系列的下一个帖子:关于生产者和消费者之间的数据传输格式。由于里面扯到了protobuf,想想干脆单独开一个帖子算了。

      ★protobuf是啥玩意儿?
      为了照顾从没听说过的同学,照例先来扫盲一把。
      首先,protobuf是一个开源项目(官方站点在“这里”),而且是后台很硬的开源项目。网上现有的大部分(至少80%)开源项目,要么是某人单干、要么是几个闲杂人等合伙搞。而protobuf则不然,它是鼎鼎大名的Google公司开发出来,并且在Google内部久经考验的一个东东。由此可见,它的作者绝非一般闲杂人等可比。
      那这个听起来牛X的东东到底有啥用处捏?简单地说,这个东东干的事儿其实和XML差不多,也就是把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议格式等场合。有同学可能心理犯嘀咕了:放着好好的XML不用,干嘛重新发明轮子啊?!先别急,后面俺自然会有说道。
      话说到了去年(大约是08年7月),Google突然大发慈悲,把这个好东西贡献给了开源社区。这下,像俺这种喜欢捡现成的家伙可就有福啦!貌似喜欢捡现成的家伙还蛮多滴,再加上Google的号召力,开源后不到一年,protobuf的人气就已经很旺了。所以俺为了与时俱进,就单独开个帖子来忽悠一把。

      ★protobuf有啥特色?
      扫盲完了之后,就该聊一下技术方面的话题了。由于这玩意儿发布的时间较短(未满周岁),所以俺接触的时间也不长。今天在此是先学现卖,列位看官多多包涵 :-)

      ◇性能好/效率高
      现在,俺就来说说Google公司为啥放着好端端的XML不用,非要另起炉灶,重新造轮子。一个根本的原因是XML性能不够好。
      先说时间开销:XML格式化(序列化)的开销倒还好;但是XML解析(反序列化)的开销就不敢恭维啦。俺之前经常碰到一些时间性能很敏感的场合,由于不堪忍受XML解析的速度,弃之如敝履。
      再来看空间开销:熟悉XML语法的同学应该知道,XML格式为了有较好的可读性,引入了一些冗余的文本信息。所以空间开销也不是太好(不过这点缺点,俺不常碰到)。
      由于Google公司赖以吹嘘的就是它的海量数据和海量处理能力。对于几十万、上百万机器的集群,动不动就是PB级的数据量,哪怕性能稍微提高0.1%也是相当可观滴。所以Google自然无法容忍XML在性能上的明显缺点。再加上Google从来就不缺造轮子的牛人,所以protobuf也就应运而生了。
      Google对于性能的偏执,那可是出了名的。所以,俺对于Google搞出来protobuf是非常滴放心,性能上不敢说是最好,但肯定不会太差。

      ◇代码生成机制
      除了性能好,代码生成机制是主要吸引俺的地方。为了说明这个代码生成机制,俺举个例子。
      比如有个电子商务的系统(假设用C++实现),其中的模块A需要发送大量的订单信息给模块B,通讯的方式使用socket。
    假设订单包括如下属性:
    --------------------------------
      时间:time(用整数表示)
      客户id:userid(用整数表示)
      交易金额:price(用浮点数表示)
      交易的描述:desc(用字符串表示)
    --------------------------------
      如果使用protobuf实现,首先要写一个proto文件(不妨叫Order.proto),在该文件中添加一个名为"Order"的message结构,用来描述通讯协议中的结构化数据。该文件的内容大致如下:

     

    --------------------------------

    message Order
    {
      required int32 time = 1;
      required int32 userid = 2;
      required float price = 3;
      optional string desc = 4;
    }

    --------------------------------

     


      然后,使用protobuf内置的编译器编译 该proto。由于本例子的模块是C++,你可以通过protobuf编译器的命令行参数(看“这里 ”),让它生成C++语言的“订单包装类”。(一般来说,一个message结构会生成一个包装类)
      然后你使用类似下面的代码来序列化/解析该订单包装类:


    --------------------------------

    // 发送方

    Order order;
    order.set_time(XXXX);
    order.set_userid(123);
    order.set_price(100.0f);
    order.set_desc("a test order");

    string sOrder;
    order.SerailzeToString(&sOrder);

    // 然后调用某种socket的通讯库把序列化之后的字符串发送出去
    // ......

    --------------------------------

    // 接收方

    string sOrder;
    // 先通过网络通讯库接收到数据,存放到某字符串sOrder
    // ......

    Order order;
    if(order.ParseFromString(sOrder))  // 解析该字符串
    {
      cout << "userid:" << order.userid() << endl
              << "desc:" << order.desc() << endl;
    }
    else
    {
      cerr << "parse error!" << endl;
    }

    --------------------------------

     


      有了这种代码生成机制,开发人员再也不用吭哧吭哧地编写那些协议解析的代码了(干这种活是典型的吃力不讨好)。
      万一将来需求发生变更,要求给订单再增加一个“状态”的属性,那只需要在Order.proto文件中增加一行代码。对于发送方(模块A),只要增加一行设置状态的代码;对于接收方(模块B)只要增加一行读取状态的代码。哇塞,简直太轻松了!
      另外,如果通讯双方使用不同的编程语言来实现,使用这种机制可以有效确保两边的模块对于协议的处理是一致的。
      顺便跑题一下。
      从某种意义上讲,可以把proto文件看成是描述通讯协议的规格说明书(或者叫接口规范)。这种伎俩其实老早就有了,搞过微软的COM编程或者接触过CORBA的同学,应该都能从中看到IDL(详细解释看“这里 ”)的影子。它们的思想是相通滴。

      ◇支持“向后兼容”和“向前兼容”
      还是拿刚才的例子来说事儿。为了叙述方便,俺把增加了“状态”属性的订单协议成为“新版本”;之前的叫“老版本”。
      所谓的“向后兼容”(backwardcompatible),就是说,当模块B升级了之后,它能够正确识别模块A发出的老版本的协议。由于老版本没有“状态”这个属性,在扩充协议时,可以考虑把“状态”属性设置成非必填 的,或者给“状态”属性设置一个缺省值(如何设置缺省值,参见“这里 ”)。
      所谓的“向前兼容”(forward compatible),就是说,当模块A升级了之后,模块B能够正常识别模块A发出的新版本的协议。这时候,新增加的“状态”属性会被忽略。
      “向后兼容”和“向前兼容”有啥用捏?俺举个例子:当你维护一个很庞大的分布式系统时,由于你无法同时 升级所有 模块,为了保证在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的“向后兼容”或“向前兼容”。

      ◇支持多种编程语言
      俺开博以来点评的几个开源项目(比如“Sqlite ”、“cURL ”),都是支持很多种 编程语言滴,这次的protobuf也不例外。在Google官方发布的源代码中包含了C++、Java、Python三种语言(正好也是俺最常用的三种,真爽)。如果你平时用的就是这三种语言之一,那就好办了。
      假如你想把protobuf用于其它语言,咋办捏?由于Google一呼百应的号召力,开源社区对protobuf响应踊跃,近期冒出很多其它编程语言的版本(比如ActionScript、C#、Lisp、Erlang、Perl、PHP、Ruby等),有些语言还同时搞出了多个开源的项目。具体细节可以参见“这里 ”。
      不过俺有义务提醒一下在座的各位同学。如果你考虑把protobuf用于上述这些语言,一定认真评估对应的开源库。因为这些开源库不是Google官方提供的、而且出来的时间还不长。所以,它们的质量、性能等方面可能还有欠缺。

      ★protobuf有啥缺陷?
      前几天刚刚在“光环效应 ”的帖子里强调了“要同时评估优点和缺点”。所以俺最后再来批判一下这玩意儿的缺点。
      ◇应用不够广
      由于protobuf刚公布没多久,相比XML而言,protobuf还属于初出茅庐。因此,在知名度、应用广度等方面都远不如XML。由于这个原因,假如你设计的系统需要提供若干对外的接口给第三方系统调用,俺奉劝你暂时不要考虑protobuf格式。
      ◇二进制格式导致可读性差
      为了提高性能,protobuf采用了二进制格式进行编码。这直接导致了可读性差的问题(严格地说,是没有可读性)。虽然protobuf提供了TextFormat这个工具类(文档在“这里 ”),但终究无法彻底解决此问题。
      可读性差的危害,俺再来举个例子。比如通讯双方如果出现问题,极易导致扯皮(都不承认自己有问题,都说是对方的错)。俺对付扯皮的一个简单方法就是直接抓包并dump成log,能比较容易地看出错误在哪一方。但是protobuf的二进制格式,导致你抓包并直接dump出来的log难以看懂。
      ◇缺乏自描述
      一般来说,XML是自描述的,而protobuf格式则不是。给你一段二进制格式的协议内容,如果不配合相应的proto文件,那简直就像天书一般。
      由于“缺乏自描述”,再加上“二进制格式导致可读性差”。所以在配置文件方面,protobuf是肯定无法取代XML的地位滴。

      ★为什么俺会用上protobuf?
      俺自从前段时间接触了protobuf之后,就着手把俺负责的产品中的部分数据传输协议替换成protobuf。可能有同学会问,和protobuf类似的东东也有不少,为啥独独相中protobuf捏?由于今天写的篇幅已经蛮长了,俺卖个关子,把这个话题留到“生产者/消费者模式[5]:如何选择传输协议及格式?”。俺会在后续的这个帖子里对比各种五花八门的协议格式,并谈谈俺的浅见。v



    参考CSDN博客(已关闭):http://blog.csdn.net/program_think/archive/2009/05/31/4229773.aspx



    展开全文
  • 本文主要记录了 Windows 环境下 Protobuf 的安装与基本使用教程。 更多文章欢迎访问我的个人博客–>幻境云图 1. 概述 Protocol buffers是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,与...
  • protobuf + grpc 使用入门 一

    千次阅读 2016-12-16 02:33:25
    protobuf + grpc 使用入门 一所需前提基础 maven,以及maven在eclipse中的使用,proto基础语法,grpc概念。 本文主要介绍作者重点想要使用eclispse 自动编译固定文件夹下的proto文件的方法。首先在eclipse 新建maven ...
  • grpc基本使用

    2020-10-21 22:43:22
    # 安装protobuf wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zip unzip protobuf-all-3.5.1.zip cd protobuf-3.5.1/ ./configure make make install # 检查是否安装成功 ...
  • repeated: 可重复变长字段,类似数组/list集合; 例如:repeated int32 Name=1; 引入其它proto文件:import "Protos/greet.proto"; #greet.proto与当前文件在同一个目录下,必须带上”Protos/“;...
  • grpc的入门使用

    2020-02-26 01:03:00
    女主宣言grpc是一个高性能、通用的开源RPC框架,基于HTTP/2协议标准和Protobuf序列化协议开发,支持众多的开发语言。在对接口具有严格约束或者传递大量数据的场景中得到了广泛的...
  • Protobuf3语言指南

    万次阅读 多人点赞 2017-01-25 22:14:44
    定义一个消息类型 指定字段类型 分配标识号 指定字段规则 添加更多消息类型 添加注释 保留标识符(Reserved) 从.proto文件生成了什么? 标量数值类型 默认值 枚举 使用其他消息类型 导入定义 ...包及
  • Protobuf3教程

    万次阅读 2018-06-27 11:49:15
    定义一个消息类型 指定字段类型 分配标识号 指定字段规则 添加更多消息类型 添加注释 保留标识符(Reserved) 从.proto文件生成了什么? 标量数值类型 默认值 枚举 使用其他消息类型 ...Oneof 特性...
  • Could NOT find Protobuf

    千次阅读 2019-01-14 15:46:23
    $ sudo apt-get install libprotobuf-dev protobuf-compiler $ protoc --version libprotoc 2.6.1
  • protobuf源码编译与安装

    万次阅读 2016-12-14 19:46:07
    protobuf源码编译与安装1.下载protobuf源码git clone https://github.com/google/protobuf.git2.安装编译所需的依赖sudo yum install -y autoconf automake libtool curl make g++ unzip3.产生配置脚本./autogen.sh4...
  • linux 安装protobuf,以及python版

    万次阅读 2017-11-23 13:57:21
    系统新装了python3,发现之前的的protobuf不能调用了,报这个错误: from google.protobuf.internal import enum_type_wrapperModuleNotFoundError: No module named 'google' 没有办法,只能安装较高版本的protobuf ...
  • Protobuf与JSON互相转换

    万次阅读 2015-03-14 16:28:49
    Java http://code.google.com/p/protobuf-java-format/ maven com.googlecode.protobuf-java-format protobuf-java-format 1.2 从protobuf转json Message someProto =SomeProto.getDefaultInstance
  • protobuf-2.5.0.tar.gz的下载与安装

    万次阅读 多人点赞 2014-06-17 21:35:11
    hadoop使用protocol buffer进行通信,需要下载和安装protobuf-2.5.0.tar.gz。由于现在protobuf-2.5.0.tar.gz已经无法在官网https://code.google.com/p/protobuf/downloads/list中下载了,本人将protobuf-2.5.0.tar....
  • linux安装protobuf3.6.1编译安装

    万次阅读 2018-12-03 20:42:18
    google protobuf-3.6.1是现在最新版本,添加了新的特性,看说明 下载地址 https://github.com/protocolbuffers/protobuf/releases 我下载的是protobuf-all-3.6.1.tar.gz 包 新版本不需要执行autogen.sh脚本,直接...
  • Error:Could not download protobuf-java.jar (com.google.protobuf:protobuf-java:3.4.0):No cached version available for offline mode AS的环境配置大部分依赖于Gradle,而Gradle文件又是自动配置的,所以很多....
  • Could not download protobuf-java.jar

    千次阅读 2019-06-15 17:12:28
    Error:Could not download protobuf-java.jar (com.google.protobuf:protobuf-java:3.0.0): No cached vers 正确的做法是: 配置一下整个项目的build.gradle,注意这里不是单个Moudle的build.gradle 是在工作空间...
1 2 3 4 5 ... 20
收藏数 55,651
精华内容 22,260
关键字:

protobuf