ProtoBuffer消息设计经验
来源:互联网 发布:淘宝冷光美白仪有用么 编辑:程序博客网 时间:2024/05/21 07:13
message设计
分类
消息用于client与server之间的交互,一般都是请求-响应的组合。写代码的一大原则是:类、函数等的命名尽量能体现其含义。所以,个人习惯在消息编号以及消息结构体中通过后缀REQ、Req、RSP、Rsp来却分请求、响应。有的响应没有响应内容,无须定义响应的message,这个就相当于一个ack消息。所以可以通过后缀来表示message的分类:
_REQ、Req:表示请求
_RSP、Rsp:表示请求的响应,带有响应消息
_ACK、Ack:表示请求的确认,无响应消息
例如:
message Test1Req{ optional uint64 uid = 1; optional uint32 type = 2; }message Test1Rsp{ optional int32 test = 1; }message Test2Req{ optional uint64 uid = 1; optional uint32 type = 2; }message Test2Rsp{ optional int32 test = 1; }
抽象
消息往往会有很多公用的字段,特别是响应的消息,比如消息类型、消息的序列号、响应的结果(错误码)、响应结果的描述等,可以定义一个总的Message,将公共的字段提取出来放到Message中。例如:
message Message{ required MAGIC type = 1; required fixed32 seq = 2; optional fixed32 errCode = 3; optional bytes errMsg = 4; optional Test1Req test1Req = 5; optional Test1Rsp test1Rsp = 6; optional Test2Req test2Req = 7; optional Test2Rsp test2Rsp = 8;}
这样做的好处是:
(1)更加抽象
(2)所有消息针对Message parse一次即可,不需要再分别去parse test1Req、test1Rsp、test2Req、test2Rsp
上述代码中,请求中一般没有errcode字段,所以可以进一步抽象出Request和Response两个消息:
message Request{ optional Test1Req test1Req = 1; optional Test2Req test2Req = 2;}message Response{ required fixed32 errCode = 1; optional bytes errMsg = 2; optional Test1Rsp test1Rsp = 1; optional Test2Rsp test2Rsp = 2;}
这样的话,如有需要,请求的公用字段都加到Request中,响应的公用字段都加到Response中,两者相同的公用字段都加到上层的Message中。Message就变成如下:
message Message{ required MAGIC type = 1;//消息类型,MAGIC枚举 required fixed32 seq = 2; optional Request request = 3; optional Response response = 4;}
使用enum定义message的类型
接下来要为每一个消息定义一个消息类型,一般建议用enum定义,例如定义一个MAGIC的enum:
enum MAGIC{ MAGIC_TEST1_REQ = 0x00001001; MAGIC_TEST1_RSP = 0x00001002; MAGIC_TEST2_REQ = 0x00001003; MAGIC_TEST2_RSP = 0x00001004;}
本人喜欢用MAGIC这个命名。MAGIC(幻数)是计算机编码中的一个术语,直接翻译为魔幻一般的数字,多好听。
至于类型的数值,可以自定义,比如从1开始递增。
这样定义完后,拿到protobuffer后的字符串,一次解析,再根据type即可取出响应的对象
//代码片段 msg := Message{} err := proto.Unmarshal(buf[:len], &msg) if err != nil { //错误处理 return err } if msg.GetType() == MAGIC_TEST1_REQ{ req := msg.GetRequest().GetTest1Req()//直接取出Test1Req对象 //handle } if msg.GetType() == MAGIC_TEST2_REQ{ req := msg.GetRequest().GetTest2Req()//直接取出Test2Req对象 //handle } if msg.GetType() == MAGIC_TEST1_RSP{ rsp := msg.GetRequest().GetTest1Rsp()//直接取出Test1Rsp对象 //handle }
错误码定义
错误码定义也用enum。错误码定义的原则是:通过错误码就能直接识别是哪个模块产生的,这样定位问题的时候就直指目标。可以通过数字区间来定义不同模块的错误码:
enum ERR_CODE { CLIENT_ERR_REFUSE = 100001;//客户端的错误码从100000-19999 CLIENT_ERR_ACCEPT = 100002; SERVER1_ERR_STOP = 200001;//模块1的错误码从200000-29999 SERVER2_ERR_OVERLOAD = 300001;//模块2的错误码从300000-39999 }
序列化后数据的封包和解包
封包:定义好的Message对象通过encode之后序列化成一段buf(字符串),加上长度一起封包。一般包头前两个字节用来标示buf的长度,后面接上buf。然后发送给对端。
解包:取前两个字节先解出长度,再读入相应长度的buf,parse成Message对象即可。
proto的维护
实际应用中,不同的模块增加、修改消息或者字段是很频繁的事情。如果多个人修改proto文件,容易导致不同模块上运行不同版本proto,会有一些不可预估的风险。所以一般指定一个人负责proto的维护,所有proto的修改都由一个人完成,且制定proto更新时涉及模块的发版顺序。
- ProtoBuffer消息设计经验
- protobuffer
- 设计经验
- 设计经验
- 设计经验
- 将mysql中查询出来的数据 序列化到protobuffer消息结构体中
- php protobuffer
- protobuffer java
- erlang protobuffer
- google protobuffer
- protobuffer 编译
- java---protobuffer
- linux 消息队列使用经验
- ktv 系统设计经验
- ktv 系统设计经验
- 数据库设计经验
- 数据库设计经验
- 数据库设计经验
- 【反序表+树状数组】Codeforces Round #441(Div.2)D[Sorting the Coins]题解
- 深度学习人工智能/大数据/云计算-书籍整理
- 添加路由
- Python 递归
- webstorm快捷键大全(亲自整理)
- ProtoBuffer消息设计经验
- 风险管理方法论和选择审计方法
- Sublime text3常用插件
- 用AVCodecParameters代替AVCodecContext
- git 撤销merge操作 (是merge操作,不是push)
- GIthub访问速度慢的解决方法
- 【算法】有向无环图
- Rust日志学习(四)——simplelog
- 文章标题