• 分布式环境中,模块数据交互协议分析 (百度brpc)


    1. 背景

    之前听到同事说,要为自己的模块考虑写个数据协议。今天有空想了一下。写出来,方便后续使用。
    开源代码brpc中可以支持多种协议,nshead、redis、mongo等20多种协议。

    2. 什么是数据交互协议?

    这里说的协议,不是tcp/ip这些网络协议。
    在分布式环境中,我们需要将模块的数据通过网络bit流传给上、下游模块,就会涉及到数据完整性正确性校验。
    为了能够校验数据,就需要定义数据交换协议。

    3. 代码brpc中的实现

    每种协议类型,都需要实现自己的parser类,进行消息的验证。

    3.1 bprc 中nshead协议的校验

    nshead_t 结构体

    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);
    }
    

    3.2 bprc 中redis协议的校验

    先看看redis中的协议,比如下面主从复制时需要的select db的情形。表示有2行(*2),第一行len:6, vak:SELECT, 第二行len:2, val:10

    *2
    
    $6
    
    SELECT
    
    $2
    
    10
    
    

    校验过程:字符串处理,switch ...case...

    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>
    "
        case '+': { // Simple String  "+<string>
    "
        ......
    

    4. 如果要自己实现一种协议

    可以学习上面的两种情况:
    (1) nshead 使用特殊magic数组, bodylen,body
    (2) redis 使用val_len, val

    这也是通用的套路,len + value限定了一个变量。
    当然可以加一些crc校验和,等其他条件。

    5. 参考:

    brpc new_protocol.md

  • 相关阅读:
    Python并行编程(十三):进程池和mpi4py模块
    Python 列表集合 字典推导式、生成器表达式
    Python 迭代对象、迭代器
    Python 参数,嵌套函数 的变量 使用
    Python 编码进阶
    Python 深浅Copy
    Python 代码块、缓存机制
    Python 列表,字典 相关方法
    初识 python 字符串 相关函数
    初识编码格式
  • 原文地址:https://www.cnblogs.com/xudong-bupt/p/9496887.html
Copyright © 2020-2023  润新知