文档章节

Protocol Buffer使用简介

zhangzhihai
 zhangzhihai
发布于 2017/01/09 17:20
字数 2301
阅读 24
收藏 0

1.概览

1.1 什么是protocol buffer

protocol buffer是google的一个开源项目,它是用于结构化数据串行化的灵活、高效、自动的方法,例如XML,不过它比xml更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。

2.使用

2.1定义一个消息类型

message SearchRequest 
{
  required string query = 1;
  optional int32 page_number = 2;// Which page number do we want?
  optional int32 result_per_page = 3;// Number of results to return per page.
}

该消息定义了三个字段,两个int32类型和一个string类型的字段,每个字段由字段限制,字段类型,字段名和Tag四部分组成.对于C++,每一个.proto文件经过编译之后都会对应的生成一个.h和一个.cc文件.

字段限制

字段限制共有3类:
required:必须赋值的字段
optional:可有可无的字段
repeated:可重复字段(变长字段),类似于数组

repeated类型相当于std的vector,可以用来存放N个相同类型的内容


由于一些历史原因,repeated字段并没有想象中那么高效,新版本中允许使用特殊的选项来获得更高效的编码:

repeated int32 samples = 4 [packed=true];

Tags

消息中的每一个字段都有一个独一无二的数值类型的Tag.1到15使用一个字节编码,16到2047使用2个字节编码,所以应该将Tags 1到15留给频繁使用的字段.
可以指定的最小的Tag为$$1$$,最大为$$2^{29}-1$$或$$536,870,911$$.但是不能使用$$19000$$到$$19999$$之间的值,这些值是预留给protocol buffer的.

注释

使用C/C++的//语法来添加字段注释.

2.2 值类型

proto的值类型与具体语言中值类型的对应关系.

2.3 可选字段与缺省值

在消息解析时,如果发现消息中没有包含可选字段,此时会将消息解析对象中相对应的字段设置为默认值,可以通过下面的语法为optional字段设置默认值:

optional int32 result_per_page = 3 [default = 10];

如果没有指定默认值,则会使用系统默认值,对于string默认值为空字符串,对于bool默认值为false,对于数值类型默认值为0,对于enum默认值为定义中的第一个元素.

2.4 枚举

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

由于枚举值采用varint编码,所以为了提高效率,不建议枚举值取负数.这些枚举值可以在其他消息定义中重复使用.

2.5 使用其他消息类型

可以使用一个消息的定义作为另一个消息的字段类型.

message Result 
{
  required string url = 1;
  optional string title = 2;
  repeated string snippets = 3;
}

message SearchResponse 
{
  repeated Result result = 1;
}

可以使用import语法来包含另外一个.proto文件.

import "myproject/other_protos.proto";

2.6 嵌套类型

在protocol中可以定义如下的嵌套类型

message SearchResponse 
{
  message Result 
  {
    required string url = 1;
    optional string title = 2;
    repeated string snippets = 3;
  }
  repeated Result result = 1;
}

如果在另外一个消息中需要使用Result定义,则可以通过Parent.Type来使用.

message SomeOtherMessage 
{
  optional SearchResponse.Result result = 1;
}

protocol支持更深层次的嵌套和分组嵌套,但是为了结构清晰起见,不建议使用过深层次的嵌套,建议通过 2.5 小节提到的方法来实现.

2.7 更新一个数据类型

在更新一个数据类型时更多的是需要考虑与旧版本的兼容性问题:

  1. 不要改变任何已存在字段的Tag值,如果改变Tag值可能会导致数值类型不匹配,具体原因参加protocol编码
  2. 建议使用optionalrepeated字段限制,尽可能的减少required的使用.
  3. 不需要的字段可以删除,删除字段的Tag不应该在新的消息定义中使用.
  4. 不需要的字段可以转换为扩展,反之亦然只要类型和数值依然保留
  5. int32, uint32, int64, uint64, 和bool是相互兼容的,这意味着可以将其中一种类型任意改编为另外一种类型而不会产生任何问题
  6. sint32sint64是相互兼容的
  7. stringbytes是相互兼容的
  8. fixed32 兼容 sfixed32, fixed64 兼容 sfixed64.
  9. optional 兼容repeated

2.8 扩展

extend特性来让你声明一些Tags值来供第三方扩展使用.

message Foo 
{
  // ...
  extensions 100 to 199;
}

假如你在你的proto文件中定义了上述消息,之后别人在他的.proto文件中import你的.proto文件,就可以使用你指定的Tag范围的值.

extend Foo 
{
  optional int32 bar = 126;
}

在访问extend中定义的字段和,使用的接口和一般定义的有点不一样,例如set方法:

    Foo foo;
    foo.SetExtension(bar, 15);

类似的有HasExtension(), ClearExtension(), GetExtension(), MutableExtension(), and AddExtension()等接口.

2.9 选项

  • optimize_for (file option): 可以设置的值有SPEED, CODE_SIZE, 或 LITE_RUNTIME. 不同的选项会以下述方式影响C++, Java代码的生成.T
    • SPEED (default): protocol buffer编译器将会生成序列化,语法分析和其他高效操作消息类型的方式.这也是最高的优化选项.确定是生成的代码比较大.
    • CODE_SIZE: protocol buffer编译器将会生成最小的类,确定是比SPEED运行要慢
    • LITE_RUNTIME: protocol buffer编译器将会生成只依赖"lite" runtime library (libprotobuf-lite instead of libprotobuf)的类. lite运行时库比整个库更小但是删除了例如descriptors 和 reflection等特性. 这个选项通常用于手机平台的优化.
option optimize_for = CODE_SIZE;

3.常用API介绍

对于如下消息定义:

// test.proto
message PBStudent 
{    
    optional uint32 StudentID   = 1;
    optional string Name        = 2;
    optional uint32 Score       = 3;
}    

message PBMathScore
{    
    optional uint32 ClassID     = 1;  
    repeated PBStudent ScoreInf   = 2;
}

protocol buffer编译器会为每个消息生成一个类,每个类包含基本函数,消息实现,嵌套类型,访问器等部分.

3.1 基本函数

public:
 PBStudent();
 virtual ~PBStudent();

 PBStudent(const PBStudent& from);

 inline PBStudent& operator=(const PBStudent& from) {
   CopyFrom(from);
   return *this;
 }

 inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
   return _unknown_fields_;
 }

 inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
   return &_unknown_fields_;
 }

 static const ::google::protobuf::Descriptor* descriptor();
 static const PBStudent& default_instance();

 void Swap(PBStudent* other);

3.2 消息实现

PBStudent* New() const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
void CopyFrom(const PBStudent& from);
void MergeFrom(const PBStudent& from);
void Clear();
bool IsInitialized() const;                                                                          

int ByteSize() const;
bool MergePartialFromCodedStream(
    ::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
    ::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
int GetCachedSize() const { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const;

3.3 嵌套类型

3.4 访问器

// optional uint32 StudentID = 1;
inline bool has_studentid() const;
inline void clear_studentid();
static const int kStudentIDFieldNumber = 1;
inline ::google::protobuf::uint32 studentid() const;
inline void set_studentid(::google::protobuf::uint32 value);

// optional string Name = 2;
inline bool has_name() const;                                
inline void clear_name();
static const int kNameFieldNumber = 2;
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline void set_name(const char* value, size_t size);
inline ::std::string* mutable_name();
inline ::std::string* release_name();
inline void set_allocated_name(::std::string* name);

// optional uint32 Score = 3;                            
inline bool has_score() const;
inline void clear_score();
static const int kScoreFieldNumber = 3;
inline ::google::protobuf::uint32 score() const;
inline void set_score(::google::protobuf::uint32 value);

protocol buffer编译器会对每一个字段生成一些getset方法,这些方法的名称采用标识符所有小写加上相应的前缀或后缀组成.生成一个值为Tags的k标识符FieldNum常量,

3.5 其他函数

除了生成上述类型的方法外, 编译器还会生成一些用于消息类型处理的私有方法. 每一个.proto文件在编译的时候都会自动包含message.h文件,这个文件声明了很多序列化和反序列化,调试, 复制合并等相关的方法.

3.6 使用例子

在我们平时的使用中,通常一个message对应一个类,在对应的类中定义一个set和create方法来生成和解析PB信息.针对上述消息定义如下类:

// test.h
class CStudent
{
public:
    unsigned    mStudentID;
    unsigned    mScore;
    string      mName;

    CStudent()
    {
        Init();
    }

    inline void Init()
    {
        mStudentID = 0;
        mScore = 0;
        mName = "";
    }
}

class CMathScore
{
private:
    unsigned    mClassID;
    CStudent    mScoreInf[100];
public:
    CMathSCore()
    {
        Init();
    }
    ~CMathScore() {};

    void Init();
    void SetFromPB(const PBMathScore* pPB);
    void CreatePB(PBMathScore* pPB);

    // Get & Set mClassID
    ...
    // Get & set mScoreInf
    ...
    // some other function
    ...
}

对应的cpp文件中实现对PB的操作

// test.cpp
void CMathScore::Init()
{
    mClassID = 0;
    memset(mScoreInf, 0, sizeof(mScoreInf));
}

void CMathScore::SetFromPB(const PBMathScore* pPB)
{
    if ( NULL == pPB ) return;

    mClassID = pPB->classid();
    for(unsigned i = 0; i < (unsigned)pPB->scoreinf_size() && i < 100; ++i)
    {
        PBStudent* pStu = pPB->mutable_scoreinf(i);
        mScoreInf[i].mStudentID = pStu->studentid();
        mScoreInf[i].mScore        = pStu->score();
        mScoreInf[i].mName        = pStu->name();
    }
}

void CMathScore::CreatePB(PBMathScore* pPB)
{
    if ( NULL == pPB ) return;

    pPB->set_classid(mClassID);
    for(unsigned i = 0; i < 100; ++i)
    {
        PBStudent* pStu = pPB->add_scoreinf();
        pStu->set_studentid(mScoreInf[i].mStudentID)
        pStu->set_score(mScoreInf[i].mScore);
        pStu->set_name(mScoreInf[i].mName);        
    }
}

PB文件的读写

// use.cpp
#include<test.h>

#defind        MAX_BUFFER        1024 * 1024
int write()
{
    CMathScore    mMath;
    PBMathScore mPBMath;
    // use set functions to init member variable

    fstream fstm("./math.dat", ios::out | ios::binary);
    if ( fstm.is_open() == false )
    {
        return -1;
    }    
    char* tpBuffer = (char*)malloc(MAX_BUFFER);
    if ( NULL == tpBuffer )
    {
        return -2;
    }

    mMath.CreatePB(&mPBMath);
    if ( mPBMath.SerializeToArray(tpBuffer, mPBMath.ByteSize()) == false )
    {
        return -3;
    }
    fstm.write(tpBuffer, mPBMath.ByteSize());
    free(tpBuffer);
    fstm.close();

    return 0;
}

int read()
{
    CMathScore    mMath;
    PBMathScore mPBMath;

    fstream fstm.open("./math.dat", ios::out | ios::binary);
    if ( fstm.is_open() == false )
    {
        return -1;
    }    
    char* tpBuffer = (char*)malloc(MAX_BUFFER);
    if ( NULL == tpBuffer )
    {
        return -2;
    }
    char*    tpIdx = tpBuffer;
    int     tLen;
    while ( !fstm.eof() && tLen < MAX_BUFFER )
    {
        fstm.read(tpIdx, 1);
        tpIdx += 1;
        tLen++;
    }
    if ( mPBMath.ParseFromArray(tpBuffer, tLen - 1) == false )
    {
        return -3;
    }
    fstm.close();
    free(tpBuffer);
    tpIdx = NULL;

    mMath.SetFromPB(&mPBMath);
    // do some thing

    return 0;
}

本文转载自:http://www.jianshu.com/p/b1f18240f0c7

zhangzhihai
粉丝 2
博文 18
码字总数 2207
作品 0
浦东
高级程序员
私信 提问
Protocol Buffers 简介

文档编辑和持续集成状态: 本文档的 Protocol Buffer 的中文文档使用的是 Asciidoctor 进行编排的 http://docs.ossez.com/protocol-buffers-docs/index.html(本 WIKI 中的内容将会与在线发布...

honeymoose
07/24
22
0
远程通信协议:从 CORBA 到 gRPC

摘要 一、远程调用技术简史 二、gRPC 简介 三、gRPC 示例代码 自从产业界发明机器联网的那一天就已经开始探索最优的远程通信机制。操作系统如 UNIX、Windows 和 Linux 等都有实现远程通信的内...

RiboseYim
2017/11/01
0
0
Google Protocol Buffer 和 gRPC 简介

更多文章请访问独立博客 https://huangwenwei.com Protocol Buffer Protocol buffer 是谷歌推出的一种轻便高效的结构化数据存储格式,把结构化的数据序列化。常用以存储数据、作为网络通信的...

hww_面条酱
2017/10/18
0
0
Android:手把手教你学会使用Google出品的序列化神器Protocol Buffer

前言 习惯用 数据存储格式的你们,相信大多都没听过 其实 是 出品的一种轻量 & 高效的结构化数据存储格式,性能比 真的强!太!多! 由于 出品,我相信已经具备足够的吸引力 今天,我将详细介...

Carson_Ho
2018/04/16
0
0
protocol buffers入门基础知识(一)

protocol buffers简介 下载地址 2.protocol buffers优势 3.Protocol Buffers 3.0 技术手册 4.一个pb的demo 执行编译命令生成文件...

Cobbage
2018/10/01
53
0

没有更多内容

加载失败,请刷新页面

加载更多

使用TensorFlow的AI程序运行报错AttributeError: module 'tensorflow' has no attribute 'xxx'

使用TensorFlow的AI程序,在运行时报错AttributeError: module 'tensorflow' has no attribute 'xxx',首先检查是否是包路径不对,一般是版本变化所致。...

织梦之魂
58分钟前
3
0
提示浏览器版本低

本文转载于:专业的前端网站➭提示浏览器版本低 网站网页在遇到浏览器低版本(尤其是IE浏览器)时,提示浏览器版本低(如IE8以及以下),建议用户升级浏览器以获得最好体验。以下是代码: 1...

前端老手
今天
6
0
CentOS 7系统增加swap

转载请注明文章出处:CentOS 7系统增加swap swap是位于磁盘上的特殊文件(或分区),属于“虚拟内存”的一部分。通俗点就是内存的备胎,内存充足的情况下,基本上没swap什么事(和设置有关)...

tlanyan
今天
6
0
基于Prometheus和Grafana的监控平台 - 环境搭建

相关概念 微服务中的监控分根据作用领域分为三大类,Logging,Tracing,Metrics。 Logging - 用于记录离散的事件。例如,应用程序的调试信息或错误信息。它是我们诊断问题的依据。比如我们说...

JAVA日知录
今天
6
0
PHP运行时全局构造体

struct _php_core_globals { zend_bool magic_quotes_gpc; // 是否对输入的GET/POST/Cookie数据使用自动字符串转义。 zend_bool magic_quotes_runtime; //是否对运行时从外部资源产生的数据使...

冻结not
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部