ProtoBuf 协议设计与开发

周日本来要去爬山的,但是没去成,突然想写点东西,但本人文采不好,只能闲扯一点技术方面的文章,整理了下有道笔记,然后最近一直在开发protobuf的协议接口,就写写ProtoBuf相关的东西吧。

本文精髓:

   protobuf的消息设计

   消息分发设计Message Dispatch

   针对程序升级的proto设计

文章末尾对着三点做详细说明。

     最近一段时间在写linux服务端接口程序,刚开始如果按照需求的话,大概有十个接口左右,但是后面慢慢分解需求后,其实真正就只有五个接口。

    编码工作早早完成,进入到测试阶段,一般情况可能会等web实现完成后,然后借助web client,在做调试,但是这期间工作效率不高,而且存在很多问题,可能后台接口没实现好,也有可能web没实现好。作为linux后台服务开发,需要会模拟客户单发送数据,如果接口很简单的话,可以直接使用telnet工具。

    现在最为流行的后台服务端通信的协议有:JSON、XML、ProtoBuf,当协议为这三种的时候,简单的telnet就不能胜任了,使用JSON和ProtoBuf的话,先借助工具做序列化工作,使用XML的话,也要事先编写好XML。

    为了更加高效的对后台服务接口做好单元测试,也为了在web开发调试的时候,提供稳定的后台服务接口,自己利用MFC写了一个小的调试工具


参数设置里面输入的是json串,因为这个有很多工具方便序列化,如:https://www.bejson.com/jsoneditoronline/

    这个工具很简单,从界面就三个输入,IP、port、消息类型,主要工作就是模拟客户端向服务端发送消息:

        需要借用服务端的proto协议文件

        序列化protobuf

        根据消息类型做消息分发

    这里用到protobuf,先大概说一下它的使用与原理

    1,介绍安装

        直接去百度,这里就跳过

    2,编写 .proto文件

        来个例子:

        package lm;

        message helloworld

        {

        required int32     id = 1;  // ID

        required string    str = 2;  // str

        optional int32     opt = 3;  //optional field

        }

限定修饰符 + 数据类型 + 字段名称 = 字段编码值

尽量养成良好测编程习惯,对proto文件命名与消息名做规范的命名

    在上例中,package 名字叫做 lm,定义了一个消息 helloworld,该消息有三个成员,类型为 int32 的 id,另一个为类型为 string 的成员 str。opt 是一个可选的成员,即消息中可以不包含该成员。

    3,编译.proto文件

    写好 proto 文件之后就可以用 Protobuf 编译器将该文件编译成目标语言了。本例中我们将使用 C++。假设您的 proto 文件存放在 $SRC_DIR 下面,您也想把生成的文件放在同一个目录下,则可以使用如下命令:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto

命令将生成两个文件:

        lm.helloworld.pb.h , 定义了 C++ 类的头文件

        lm.helloworld.pb.cc , C++ 类的实现文件

    在生成的头文件中,定义了一个 C++ 类 helloworld

    4,序列化

    在第三部编译生成的cc文件中,有一系列的SerializeToXXX方法,如SerializeToArray,可以根据具体情况用这一系列方法进行序列化。

    5,反序列化

    在第3个步骤编译生成的cc文件中,有一系列的ParseFromXXX方法,如ParseFromArray,可以根据具体情况用这一系列方法进行反序列化。

    掌握以上几个步骤基本就能搞定proto的开发了,进阶的话可以去掌握proto的数据类型以及requried、optional、repeated限定修饰符,和message的嵌套(message嵌套可以设计出更多复杂的协议,满足更复杂的需求)。

    protobuf的优劣自己去百度,JSON,XML我也有用过,但是相对来说,谷歌的protobuf是我用起来最方便高效的。

这是网上的一个测试结果:http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking


    回到文章开头部分说的精髓,现在逐一到来

    一,proto的设计:

    一般设计规则如下

    message Request

    {

    required fixed64 msgtype = 1;

    required bytes bodys = 2;

    }

    一个消息类型加一个消息体,但这不能满足复杂的业务需求,所以复杂的系统里面一般拆成这样:

    message Header

    {

    required fixed64 msgtype = 1;

    }

    message HelloworldRequest

    {

    required int32     id = 1;  // ID

    required string    str = 2;  // str

    optional int32     opt = 3;  //optional field

    }

    一个消息类型(单独定义一个文件)对应一个请求消息,一个请求消息对应一个接口,消息里面的字段对应接口所需要的参数,这是常用的设计方法,能满足所有的业务需求。

    二,消息分发的设计

这里的message dispatch是指程序根据不同的msgtype,反序列化proto和做不同的业务逻辑处理。

    古老而又传统的设计是采用switch case来做(我曾经看到有在用if else的),这种方法有很多不足之处,我这里举几个很常见的:

    1,代码臃肿,随着消息类型的增加,会有n多的case

    2,假设case里漏掉了break;那就不妙了。

    3,代码维护差,每次增加msgtype,除了实现对应的业务逻辑处理,还要到消息入口增加对应的case。

在c语言里面,有一种很实用的办法,那就是函数指针,很多开源的和上层应用的回调函数或方法的底层都是封装了c语言的函数指针,这里提到函数指针,我简单介绍一下(本文没有用很大篇幅来说明函数指针,掌握其基本定义,慢慢学会衍生到复杂的概念):

        从概念上说,函数指针是指向函数的指针变量,它本质上是一个指针变量。

        其广泛的定义是: int (*f) (int x);

        复制和调用: int func(int x);   f = func;

    那么函数指针在Message Dispath 如何设计呢,还是来一个简单的例子:

    定义一个结构体

        struct SMsgCmd {

        int msgtype;                             /*msg type*/

        int (*func)(const char *argv);           /* handler * 函数指针/

        };

    定义一个结构体数组,并初始化

        static struct SMsgCmd commands[] = {

        { 1,   Request1},

        { 2,   Request1},

        };

    在服务程序的消息入口处,遍历改数组即可

        for ()

        {

        if (msgtype == commands[i].msgtype){

        (*commands[i].func)(argv);

        }

        }

在c++11里面,可以利用std::function 和std::bind,其原理跟函数指针一个道理。

    三,针对程序升级的proto设计

    程序升级是常有的事,但我们升级的时候需要考虑兼容性,之前有看到过同个版本号如

    if (version == 1){

    }

    else if (version > 2)

    .........

    这样做的缺陷我就不在过多的说明了。

    对于proto的协议来说,我们只要做到以下几点,就会完美兼容新旧版本

1,不要随意添加或删除 required限定词修饰的字段

        2,不要随意改变现有字段编码值

        3,若需要新增字段,请用optional限定词修饰

本文到此就算结束了,若有错误之处,请多多指教!

欢迎关注本人微信公众号:lzyTalk江湖,不只是谈江湖,还会分享很多技术干货哦!


你可能感兴趣的:(ProtoBuf 协议设计与开发)