以前听到同事说,要为本身的模块考虑写个数据协议。今天有空想了一下。写出来,方便后续使用。
开源代码brpc
中能够支持多种协议,nshead、redis、mongo等20多种协议。git
这里说的协议,不是tcp/ip这些网络协议。
在分布式环境中,咱们须要将模块的数据经过网络bit流传给上、下游模块,就会涉及到数据完整性
、正确性
校验。
为了可以校验数据,就须要定义数据交换协议。github
每种协议类型,都须要实现本身的parser类,进行消息的验证。redis
nshead_t 结构体shell
static const unsigned int NSHEAD_MAGICNUM = 0xfb709394; //特殊数字 struct nshead_t { unsigned short id; unsigned short version; unsigned int log_id; char provider[16]; unsigned int magic_num; unsigned int reserved; unsigned int body_len; //实际传输的包体长度 };
校验过程:magic_num是否正确,是否包体超长,是否包体收到数据不足等。数组
ParseResult ParseNsheadMessage(butil::IOBuf* source, Socket*, bool /*read_eof*/, const void* /*arg*/) { char header_buf[sizeof(nshead_t)]; const size_t n = source->copy_to(header_buf, sizeof(header_buf)); if (n < offsetof(nshead_t, magic_num) + 4) { return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA); } const void* dummy = header_buf + offsetof(nshead_t, magic_num); const unsigned int magic_num = *(unsigned int*)dummy; if (magic_num != NSHEAD_MAGICNUM) { RPC_VLOG << "magic_num=" << magic_num << " doesn't match NSHEAD_MAGICNUM=" << NSHEAD_MAGICNUM; return MakeParseError(PARSE_ERROR_TRY_OTHERS); } if (n < sizeof(nshead_t)) { return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA); } const nshead_t* nshead = (const nshead_t *)header_buf; uint32_t body_len = nshead->body_len; if (body_len > FLAGS_max_body_size) { return MakeParseError(PARSE_ERROR_TOO_BIG_DATA); } else if (source->length() < sizeof(header_buf) + body_len) { return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA); } policy::MostCommonMessage* msg = policy::MostCommonMessage::Get(); source->cutn(&msg->meta, sizeof(header_buf)); source->cutn(&msg->payload, body_len); return MakeMessage(msg); }
先看看redis中的协议,好比下面主从复制时须要的select db的情形。表示有2行(*2),第一行len:6, vak:SELECT, 第二行len:2, val:10网络
*2\r\n $6\r\n SELECT\r\n $2\r\n 10\r\n
校验过程:字符串处理,switch ...case...tcp
bool RedisReply::ConsumePartialIOBuf(butil::IOBuf& buf, butil::Arena* arena) { // Notice that all branches returning false must not change `buf'. const char* pfc = (const char*)buf.fetch1(); if (pfc == NULL) { return false; } const char fc = *pfc; // first character switch (fc) { case '-': // Error "-<message>\r\n" case '+': { // Simple String "+<string>\r\n" ......
能够学习上面的两种状况:分布式
(1) nshead 使用特殊magic数组, bodylen,bodyide
(2) redis 使用val_len, val学习
这也是
通用的套路
,len + value限定了一个变量。
固然能够加一些crc校验和,等其余条件。