zoukankan      html  css  js  c++  java
  • 筒子们,使用Protobuf优化你的协议

    Protocol buffers是google提供的一种将结构化数据进行序列化和反序列化的方法,其优点是语言中立,平台中立,可扩展性好,目前在google内部大量用于数据存储,通讯协议等方面。PB在功能上类似XML,但是序列化后的数据更小,解析更快,使用上更简单。用户只要按照proto语法在.proto文件中定义好数据的结构,就可以使用PB提供的工具(protoc)自动生成处理数据的代码,使用这些代码就能在程序中方便的通过各种数据流读写数据。PB目前支持Java, C++和Python3种语言。另外,PB还提供了很好的向后兼容,即旧版本的程序可以正常处理新版本的数据,新版本的程序也能正常处理旧版本的数据。我们主要研究PB在C++语言的使用,PB的编译安装比较简单,C++的库使用make完成,java使用maven完成,python直接使用setup命令完成。

    Protocol Buffers要如何用在socket通信的通信协议中?可以大概地说一下:你需要根据你的协议编写一个.proto文件,此文件的格式是按Protocol Buffers的要求书写的。然后用Protocol Buffers编译器生成这个文件对应的类文件(包括一个.h文件和一个.cc文件),然后在你的程序中include生成的头文件,当需要发送socket消息的时候,先用这个类的对象的SerializeToString()方法,生成一个字符串,这个字符串也就相当于我们传统意义上的编码过的消息,然后在socket消息的接收方,使用ParseFromString()方法,就可以将消息中包含的数据解析到生成的类的成员变量中,就可以直接取出来用了。整个过程不需要你去考虑编码、解码,就算更改了协议,修改工作也非常方便。

    零、为什么要研究ProtoBuff

    需求是最大的驱动,加入百度Hi团队一个多月了,这一个多月的时间里做了许多以前没有做过的事。针对Hi用户的反馈,移动端Hi对流量的消耗太大。Hi消息传输的格式目前主要有2种,一种是公司定义的一种nshead+mcbody的形式,这种方式类似于我们今天所要讲解的ProtoBuff,统一使用这种方式也能够减小传输流量,而且数据安全性也更高;另一中方式就是比较古老的文本协议格式了。公司决定对现有的文本协议进行优化。怎么优化呢?Hi目前使用文本协议在客户端和服务端进行信息等传输,这里所说的消息包括CS之间Request包、ACK包,或者服务端的Notify包等等,这些包里面的包括的内容不仅仅是聊天信息,也包括联系人状态变更等各种通知信息。

    总得来说,Hi消息传输的报文格式可以概括为:header + body,header主要由一些键值对构成,而body则更多的是使用xml的格式。最终决定使用高大上的ProtoBuff对当前的文本协议进行改进,在这里先列出具体的实现思路:

    1. 现有的消息包括header和body两个部分,我们需要一个转换工具,分析现有报文的定义要求,生成一个消息格式xml文件:即包括了header和body的一个xml文件;
    2. 根据该xml格式生成proto的描述文件,然后通过Proto编译生成对应的.h和.cc文件;
    3. 最后就是要考虑和现有协议的转换了,庞大的系统兼容性是不可忽略的,即服务端添加一个专门协议转换的模块,分别针对上行和下行进行原有报文到pb报文的相互转换。

    一、ProtoBuff开发三部曲

    所谓开发三部曲,即:

    1. 定义描述文件,即.proto文件;
    2. 使用proto编译器生成该描述文件对应的定义文件,C++包括.h和.cc;
    3. 使用定义文件中提供的ProtoBuff的API读写消息。

    举个简单的实例,百度Hi在拉取群列表时的协议定义为group:get_list,我们在描述文件中定义如下:

    image

    我们如何去按照这个定义要求去封包呢?直接看代码:

    image

    上图中88行中的日志就是将我们的PB报文以字符串形式打印出来,便于我们调试,即DebugString方法。在日志中我们可到PB结构如下:

    image

    二、序列化与反序列化

    继续看代码,我们已经准备好了PB的封包,序列化之后就可以通过连接发出去了

    image

    百度Hi-Server在接收到PB包之后通过一系列的处理,会返回一个group:get_list的ACK包回来,我们怎么把回包拿出来并转化成我们现有的文本协议形式呢?继续看代码:

    image

    红色框图中的部分就是反序列化的过程,通过反序列化之后,我们就可以调用ProtoBuff定义的API去解析该PB报文了。

    三、完成协议的测试

    我们的测试目的就是保证PB与原协议的兼容性,换句话说就是转换后的数据一致性,我们在ImpPacket中重载了==运算符,完成比较的过程,即分别对ImpPacket中的header和body做比较,header是由一些map构成的,body则是xml构成的。我们的比较就是分别对header和body中的map和xml进行比较。

    image

    xml的比较代码如下(部分):

    image

    这就是该博文的所有内容,6个不同的协议,耗时2天时间完成,期间还踩到老代码中的一个坑,gdb调试才定位到,累觉不爱。感谢阅读,Published by Windows LiveWriter.

  • 相关阅读:
    做代码的曲线问题
    全书目录
    关于《Delphi源代码分析》的讨论
    说说“从编程到工程”专栏的由来
    CF285E Positions in Permutations
    CF264B Good Sequences
    CF115E Linear Kingdom Races
    CF633F The Chocolate Spree
    CF767C Garland
    CF559C Gerald and Giant Ches
  • 原文地址:https://www.cnblogs.com/berlin-sun/p/ProtoBuffParser.html
Copyright © 2011-2022 走看看