Protocol Buffer入门简介

一、Protocol buffers简述

特点

Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data.

  • 语言中立,平台中立
  • 序列化结构化数据

protocol核心学习点

优势

  • simpler(简单)
  • smaller(文件小)
  • faster(序列化与反序列化速度快)
  • 支持多种语言,相对于xml更易于程序处理

主要是由于采用protocol方式生成的binary format文件相比于xml格式的更小,序列化,反序列化,网络传输过程中有更大优势。

历史演变主要解决的问题

  • 协议不同版本的字段变换时带来的兼容性问题
  • 协议能够带来了更好的自描述,能够被不同语言处理

演变历程

  • 自动产生序列化与反序列化代码-》如何使用?
  • 利用protocol buffer自描述能力,用于大数据存储-》如何存储,效率问题如何?
  • 能够使用编译器能够自动生成服务端stub程序-》如何生成?

二、Proto3 buffer语法格式

暂时跳过proto2的语法格式

2.1 Defining A Message Type

如何在proto文件中定义一个消息类型?

syntax = "proto3"

message request {
    string name = 1;
    string age=2;
    int32 page_numer = 3;
    int32 result_per_page = 4;
}

proto messge主要有field types, field name以及field number构成。

其中field number 【1-15】区间占用One Byte,【16-2047】占用两个bytes,所以在message中fields个数不多的情况,尽量使用1-15之内的数值。

field number允许的值范围为【1-2的29次方-1(536870911)】,其中【19000-19999】为保留数值。

Adding Comments

proto use c/c++ style comments, //for line comments, /.../for block comment

Reserved Fields

message ReservedTest{
    reserved 2, 6, 10 to max;
    reserved "name", "age";
}

Note:不能在同一行混用field name与number,必须分开保留。

2.2 编译proto文件

对于一个初学者来说,刚开始肯定会拿到一个proto文件,拿到此文件后如何下手?大致步骤如下:

  1. 安装编译环境,protocol buffer compiler能够根据不同的客户端语言,生成对应的代码,规则如下

    For C++, the compiler generates a .h and .cc file from each .proto, with a class for each message type described in your file.

    For Java, the compiler generates a .java file with a class for each message type, as well as a special Builder classes for creating message class instances.

    Python is a little different – the Python compiler generates a module with a static descriptor of each message type in your .proto, which is then used with a metaclass to create the necessary Python data access class at runtime.

    For Go, the compiler generates a .pb.go file with a type for each message type in your file.

    For Ruby, the compiler generates a .rb file with a Ruby module containing your message types.

    For Objective-C, the compiler generates a pbobjc.h and pbobjc.m file from each .proto, with a class for each message type described in your file.

    For C#, the compiler generates a .cs file from each .proto, with a class for each message type described in your file.

    For Dart, the compiler generates a .pb.dart file with a class for each message type in your file

  2. 运行编译指令

2.3 Scalar Value Types

描述protocol文件的field types如何与其他不同语言的数据类型进行一一映射。

具体映射关系参考protocol数据类型映射表

protocol中定义的数据类型:

double,float,int32,int64,uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, string, bytes

对于不同数据类型,不同操作数据类型,在数据序列化与反序列化的时候,会对不同数据类型进行一定的优化。

2.4 Default values

在message被反序列化的时候,如果缺少特定field的描述,反序列化时就会设置对应的默认值,规则如下:

  • For strings, the default value is the empty string.—空字符串
  • For bytes, the default value is empty bytes.—空字节
  • For bools, the default value is false.-错
  • For numeric types, the default value is zero.-0
  • For enums, the default value is the first defined enum value, which must be 0.-联合第一个值必须为0
  • For message fields, the field is not set. Its exact value is language-dependent. See the generated code guide for details.

2.5 Enumerations

联合是protocol中相对复杂的type,例如

message request {
    string name = 1;
    int age = 2;
    enum Sex {
        MALE = 0;
        FEMALE = 1;
        OTHER = 2
    }
    Sex gender = 3;
}

在protocol中对enum的第一个元素作了特别的规定,必须为值为0,主要是为了解决enum的默认值问题以及兼容proto2协议。

联合字段别名

示例:

message request {
    string name = 1;
    int age = 2;
    enum Sex {
        option allow_alias = true;
        MALE = 0;
        FEMALE = 1;
        MAN = 1;
        OTHER = 2
    }
    Sex gender = 3;
}

允许联合字段别名在Java或者C语言中比较少见,protocol默认也不允许开启联合别名,如果要开启必须通过手动打开配置选项

2.6 Using Other Message Types

protocol文件之间如何相互引用已经定义好的Message?如何通过Message嵌套引用形成消息结构层次?

示例:

message Response {
    int http_code = 1;
    string ret_msg = 2;
    repeated Device data = 3;
}

message Result {
    string device_uuid = 1;
    string device_name = 2;
    float longitude = 3;
    float latitude = 4;
}

一般来说,在实际的代码开发过程中,尽量将Message颗粒度进行拆分,便于在项目中相互调用。

importing definitions

protocol也支持导入其他proto文件,通过proto文件导入方式,能够通过合并导入proto文件形成新的proto文件,可以对不通版本,用户暴露不一样的proto文件。

2.7 Unknown Fields

对于解析端,如果解析的数据流中包含一些未知字段,通常来说解析时候把未知字段给忽略了,但是在protocol3.5及以后的版本中,未知字段在解析时候会被保留而不是丢弃。

2.8 Any

protocol中提供了一种特殊的消息类型Any。 Any的意思为:字段可以为任何类型,在序列化时候就将此字段序列化为bytes。

示例:

import "google/protobuf/any.proto";

message ErrorStatus {
    string message = 1;
    repeated google.protobuf.Any details = 2;
}

在使用Any消息类型的时候,必须引入google的any这个proto文件。对于Any的使用场景需要收集总结?

2.9 Oneof

Oneof用于标记消息中某些字段同时最多只有一个能够同时存在,经常用于标记互斥的字段。

示例:

message Person {
    oneof contact {
        string mobile = 4;
        string email = 9;
    }
}

Oneof Features

注意在实际消息对象赋值的过程中,oneof特性带来值覆盖问题,最终值是最后一个oneof字段,oneof字段不允许使用repeated。

oneof使用案例

2.10 Maps

Maps也是最常见的数据类型。

示例

map<string, Person> class = 2;

protocol对map做了以下限定

  1. key必须为inegral以及string,value可以为除了map后的任何类型
  2. map不能用repeated来进行修饰
  3. 不允许有重复的key
  4. 对于只有key没有value情况,不同语言处理方式不一样

2.11 Packages

protocol为了防止定义的消息类型冲突,也引入了package概念,类似于java,c++等语言,protocol的包在转换为不同语言实现时,会有相关转换关系。

定义一个包的消息:

package com.dwx;

message Person {
}

引入一个包对象

message Class {
    com.dwx.Person stu = 1;
}

在proto文件转换为java文件时,默认用定义的package名来作为java的包名,如果想改变该规律,可以使用配置项

option java_package = "com.xx.xx"

2.12 Defining Services

proto文件中除了定义消息类型,另一个重要任务就是服务接口。编译器会根据选择的语言生成相应的接口代码以及存根代码。

常见的service示例

service SearchPerson {
    rpc Search(Person) returns(Class);
}

不同的编译插件可以快速生成对应的interface以及stub代码,需要深入研究maven或者gradle方式下如何集成相关插件,运行指令等

2.13 Options

proto buffer提供可选项参数,用于改变编译的一些默认行为,目前主要有针对java或者移动端的一些优化配置,汇总如下:

  1. java_package: 改变java的默认package名

    option java_package = "xx.xx.xx"

  2. java_ multiple_files:是否将messages,enums以及services作为内部类

    option java_ multiple_files = true

  3. java_outer _classname:指定outerclass名字,默认为proto文件名

    option java_outer _classname = "PersonStu"

  4. optimize_ for:可以被设置为SPEED,CODE_SIZE,LITE_RUNTIME。通过设置优化属性,代码产生器会针对不同的语言进行各种优化。

Custom Options:可以自己定义优化特性。

3 如何生成class

3.1 环境准备

  1. 安装编译环境,windows平台下可以选择windows protoc 3.6.1安装文件

  2. 配置系统环境变量,在path中添加bin路径,例如 D:\IDE\protoc-3.6.0-win32\bin

  3. 在cmd命令行下,输入protoc --version,回显libprotoc XXX, XXX为安装的版本号

3.2 编写.proto文件

syntax = "proto3"

message PersonRequest{

    string name = 1; //姓名
    uint32 age = 2; //年龄
    string birth_day = 3; //出生日期
    string mobile = 4; //手机号
    enum Sex {
        Female = 0;
        Male = 1;
        Other = 2;
    }
    Sex gender = 5; //define sex
}

3.3 命令行编译

指令格式如下:

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 

其中各个参数的含义如下:

  • --proto_path:简写-I, 为import文件的路径,缺省为当前文件路径。
  • --**_output,主要为编译器根据不同语言产生的代码文件,如果DST——DIR是.zip或者.jar,编译器会根据要求生成相应的文件
  • 提供一个或者多个文件作为输入文件,文件默认也是参考当前的路径。

3.4 案例演示

  1. 编写Person.proto文件
  2. 运行protoc --java_out ./rpc/out/person.jar --go_out .\rpc\out\go\ .\rpc\Person.proto,报"--go_out:protoc-gen-go:系统找不到指定的文件",主要是编译器中不包含golang的代码生成器,删除掉go的out文件即可。
  3. 重新运行protoc --java_ out ./rpc/out/person.jar .\rpc\Person.proto,即可生成对应的jar文件,主要out_dst文件夹必须为已存在,未存在的目录代码生成器不会自动创建。
  4. 可以通过修改配置项来调整生成的jar的内容

4 code style

代码约定:

  1. Message名:用驼峰,第一个字母为大写,如PersonRequest
  2. 字段名字用小写,名字用下划线之间分割,例如:birth_date
  3. 联合全部用大写
  4. service名字用大写,service中的方法名也用大写首字母+驼峰命名
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容

  • 翻译查阅外网资料过程中遇到的比较优秀的文章和资料,一是作为技术参考以便日后查阅,二是训练英文能力。此文翻译自 Pr...
    401阅读 66,528评论 1 39
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,260评论 0 10
  • 遇见,何需时间地点; 懂你,何需千言万语。 如果,懂,是一种简简单单的感动,那么相互感动可好 如果,懂,是一种难言...
    谭先生啦阅读 245评论 0 1
  • 2018年7月21日 星期六 晴 前几天,一个微信上的好友问我是不是少数民族,我的微信昵称怎么叫香秋卓玛,是不是...
    彩虹老师阅读 6,176评论 2 6
  • 什么是OAuth? OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全、...
    PHPer_阅读 3,662评论 1 6