2021SC@SDUSC BRPC源码分析(十四)Redis

2021SC@SDUSC BRPC源码分析(十四) redis

enum RedisReplyType {
    REDIS_REPLY_STRING = 1, 
    REDIS_REPLY_ARRAY = 2,
    REDIS_REPLY_INTEGER = 3,
    REDIS_REPLY_NIL = 4,
    REDIS_REPLY_STATUS = 5, 
    REDIS_REPLY_ERROR = 6
};

不同类型的回复,特别的REDIS_REPLY_STRING = 1表示散装字符串,REDIS_REPLY_STATUS = 5表示基本的字符串。


class RedisReply {
public:
    RedisReply(butil::Arena* arena);
    
    RedisReplyType type() const { return _type; }
    const RedisReply& operator[](size_t index) const;
    RedisReply& operator[](size_t index);

private:
    static const int npos;
    DISALLOW_COPY_AND_ASSIGN(RedisReply);

    void FormatStringImpl(const char* fmt, va_list args, RedisReplyType type);
    void SetStringImpl(const butil::StringPiece& str, RedisReplyType type);
    
    RedisReplyType _type;
    int _length;
    union {
        int64_t integer;
        char short_str[16];
        const char* long_str;
        struct {
            int32_t last_index;
            RedisReply* replies;
        } array;
        uint64_t padding[2]; 
    } _data;
    butil::Arena* _arena;
};

  • RedisReply响应的初始值为nil。RedisReply所有需要的内存分配在’ arena’。
  • type()回复的类型。
  • _lengthshort_str或者long_str的长度,计算响应次数。
  • 如果之前的解析在响应时暂停,last_index>= 0。
  • DISALLOW_COPY_AND_ASSIGN(RedisReply)RedisReply不拥有字段的内存,复制必须通过调用CopyFrom[Different|Same]Arena来进行复制。
  • const RedisReply& operator[](size_t index) const; RedisReply& operator[](size_t index);获取索引子回复。如果该响应不是数组或索引超出范围,则返回nil响应,并且调用堆栈不记录。

inline bool RedisReply::is_nil() const {
    return (_type == REDIS_REPLY_NIL || _length == npos);
}

如果redis回复为nil则为True。


inline bool RedisReply::is_error() const { return _type == REDIS_REPLY_ERROR; }

如果redis回复为error则为True。


inline bool RedisReply::is_integer() const { return _type == REDIS_REPLY_INTEGER; }

如果redis回复为integer则为True。


inline bool RedisReply::is_string() const
{ return _type == REDIS_REPLY_STRING || _type == REDIS_REPLY_STATUS; }

如果redis回复为string则为True。


inline bool RedisReply::is_array() const { return _type == REDIS_REPLY_ARRAY; }

如果redis回复为array则为True。


inline void RedisReply::SetNullString() {
    if (_type != REDIS_REPLY_NIL) {
        Reset();
    }
    _type = REDIS_REPLY_STRING;
    _length = npos;
}

设置响应为空字符串。


inline void RedisReply::SetNullArray() {
    if (_type != REDIS_REPLY_NIL) {
        Reset();
    }
    _type = REDIS_REPLY_ARRAY;
    _length = npos;
}

设置响应为空数组。


void RedisReply::SetArray(int size) {
    if (_type != REDIS_REPLY_NIL) {
        Reset();
    }
    _type = REDIS_REPLY_ARRAY;
    if (size < 0) {
        LOG(ERROR) << "negative size=" << size << " when calling SetArray";
        return;
    } else if (size == 0) {
        _length = 0;
        return;
    }
    RedisReply* subs = (RedisReply*)_arena->allocate(sizeof(RedisReply) * size);
    if (!subs) {
        LOG(FATAL) << "Fail to allocate RedisReply[" << size << "]";
        return;
    }
    for (int i = 0; i < size; ++i) {
        new (&subs[i]) RedisReply(_arena);
    }
    _length = size;
    _data.array.replies = subs;
}

设置数组的size元素的响应。调用SetArray后,使用operator[]访问子响应并设置其值。


inline void RedisReply::SetStatus(const butil::StringPiece& str) {
    return SetStringImpl(str, REDIS_REPLY_STATUS);
}
inline void RedisReply::FormatStatus(const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    FormatStringImpl(fmt, ap, REDIS_REPLY_STATUS);
    va_end(ap);
}

设置应答状态。


inline void RedisReply::SetError(const butil::StringPiece& str) {
    return SetStringImpl(str, REDIS_REPLY_ERROR);
}
inline void RedisReply::FormatError(const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    FormatStringImpl(fmt, ap, REDIS_REPLY_ERROR);
    va_end(ap);
}

设置响应为错误。


inline void RedisReply::SetInteger(int64_t value) {
    if (_type != REDIS_REPLY_NIL) {
        Reset();
    }
    _type = REDIS_REPLY_INTEGER;
    _length = 0;
    _data.integer = value;
}

设置该回复为整数的’ value’。


inline void RedisReply::SetString(const butil::StringPiece& str) {
    return SetStringImpl(str, REDIS_REPLY_STRING);
}
inline void RedisReply::FormatString(const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    FormatStringImpl(fmt, ap, REDIS_REPLY_STRING);
    va_end(ap);
}

将此回复设置为一个(批量的)字符串。


inline int64_t RedisReply::integer() const {
    if (is_integer()) {
        return _data.integer;
    }
    CHECK(false) << "The reply is " << RedisReplyTypeToString(_type)
                 << ", not an integer";
    return 0;
}

将响应转换为有符号的64位整数。如果响应不是整数,则记录调用堆栈并返回0。


inline const char* RedisReply::error_message() const {
    if (is_error()) {
        if (_length < (int)sizeof(_data.short_str)) { // SSO
            return _data.short_str;
        } else {
            return _data.long_str;
        }
    }
    CHECK(false) << "The reply is " << RedisReplyTypeToString(_type)
                 << ", not an error";
    return "";
}

将响应转换为错误消息。如果响应不是错误消息,调用堆栈将被记录并返回“”。


inline const char* RedisReply::c_str() const {
    if (is_string()) {
        if (_length < (int)sizeof(_data.short_str)) { // SSO
            return _data.short_str;
        } else {
            return _data.long_str;
        }
    }
    CHECK(false) << "The reply is " << RedisReplyTypeToString(_type)
                 << ", not a string";
    return "";
}

将响应转换为(c-style)字符串。如果响应不是字符串,调用堆栈将被记录并返回“”。如果包含\0的字符串没有完全打印,请使用data()代替。


inline butil::StringPiece RedisReply::data() const {
    if (is_string()) {
        if (_length < (int)sizeof(_data.short_str)) { // SSO
            return butil::StringPiece(_data.short_str, _length);
        } else {
            return butil::StringPiece(_data.long_str, _length);
        }
    }
    CHECK(false) << "The reply is " << RedisReplyTypeToString(_type)
                 << ", not a string";
    return butil::StringPiece();
}

将响应转换为StringPiece。如果响应的内容不是字符串,则记录调用堆栈并返回“”。
如果需要调用std::string,则先调用.data().as_string()分配内存。


inline size_t RedisReply::size() const {
    return _length;
}

如果该响应是数组,则返回数组中sub响应的个数,如果该响应是字符串,则返回字符串的长度,否则返回0,调用栈不记录。


ParseError RedisReply::ConsumePartialIOBuf(butil::IOBuf& buf) {
    if (_type == REDIS_REPLY_ARRAY && _data.array.last_index >= 0) {
        RedisReply* subs = (RedisReply*)_data.array.replies;
        for (int i = _data.array.last_index; i < _length; ++i) {
            ParseError err = subs[i].ConsumePartialIOBuf(buf);
            if (err != PARSE_OK) {
                return err;
            }
            ++_data.array.last_index;
        }
        _data.array.last_index = -1;
        return PARSE_OK;
    }

    const char* pfc = (const char*)buf.fetch1();
    if (pfc == NULL) {
        return PARSE_ERROR_NOT_ENOUGH_DATA;
    }
    const char fc = *pfc; 
    switch (fc) {
    case '-':   // Error          "-<message>\r\n"
    case '+': { // Simple String  "+<string>\r\n"
        butil::IOBuf str;
        if (buf.cut_until(&str, "\r\n") != 0) {
            const size_t len = buf.size();
            if (len > std::numeric_limits<uint32_t>::max()) {
                LOG(ERROR) << "simple string is too long! max length=2^32-1,"
                              " actually=" << len;
                return PARSE_ERROR_ABSOLUTELY_WRONG;
            }
            return PARSE_ERROR_NOT_ENOUGH_DATA;
        }
        const size_t len = str.size() - 1;
        if (len < sizeof(_data.short_str)) {
            
            _type = (fc == '-' ? REDIS_REPLY_ERROR : REDIS_REPLY_STATUS);
            _length = len;
            str.copy_to_cstr(_data.short_str, (size_t)-1L, 1/*skip fc*/);
            return PARSE_OK;
        }
        char* d = (char*)_arena->allocate((len/8 + 1)*8);
        if (d == NULL) {
            LOG(FATAL) << "Fail to allocate string[" << len << "]";
            return PARSE_ERROR_ABSOLUTELY_WRONG;
        }
        CHECK_EQ(len, str.copy_to_cstr(d, (size_t)-1L, 1/*skip fc*/));
        _type = (fc == '-' ? REDIS_REPLY_ERROR : REDIS_REPLY_STATUS);
        _length = len;
        _data.long_str = d;
        return PARSE_OK;
    }

  • if (_type == REDIS_REPLY_ARRAY && _data.array.last_index >= 0) {}解析子响应继续解析,解析被暂停。得到了完整的响应后,重置该指数。
  • 所有PARSE_ERROR_NOT_ENOUGH_DATA返回的分支都不能改变’ buf’。
  • fc第一个字符。
  • if (len < sizeof(_data.short_str)) {}SSO短字符串,包括空字符串。
  • 因为从’ buf’解析可能不完整。所以当完整的回复被解析并从’ buf’中切断时,返回PARSE_OK。如果’ buf’中的数据不足以解析,则返回PARSE_ERROR_NOT_ENOUGH_DATA,并保证’ buf’是不变的,因此可以在RedisReply对象上使用相同的buf反复调用该函数,直到函数返回PARSE_OK。这个属性确保在最坏的情况下解析RedisReply是O(N),其中N是在线回复的大小。相反,如果解析需要“buf”保持完整,最坏情况下的复杂度可能是O(N²)。如果解析失败,返回PARSE_ERROR_ABSOLUTELY_WRONG。

bool RedisReply::SerializeTo(butil::IOBufAppender* appender) {
    switch (_type) {
        case REDIS_REPLY_ERROR:
        case REDIS_REPLY_STATUS:
            appender->push_back((_type == REDIS_REPLY_ERROR)? '-' : '+');
            if (_length < (int)sizeof(_data.short_str)) {
                appender->append(_data.short_str, _length);
            } else {
                appender->append(_data.long_str, _length);
            }
            appender->append("\r\n", 2);
            return true;
        case REDIS_REPLY_INTEGER:
            appender->push_back(':');
            appender->append_decimal(_data.integer);
            appender->append("\r\n", 2);
            return true;
        case REDIS_REPLY_STRING:
            appender->push_back('$');
            appender->append_decimal(_length);
            appender->append("\r\n", 2);
            if (_length != npos) {
                if (_length < (int)sizeof(_data.short_str)) {
                    appender->append(_data.short_str, _length);
                } else {
                    appender->append(_data.long_str, _length);
                }
                appender->append("\r\n", 2);
            }
            return true;
        case REDIS_REPLY_ARRAY:
            appender->push_back('*');
            appender->append_decimal(_length);
            appender->append("\r\n", 2);
            if (_length != npos) {
                for (int i = 0; i < _length; ++i) {
                    if (!_data.array.replies[i].SerializeTo(appender)) {
                        return false;
                    }
                }
            }
            return true;
        case REDIS_REPLY_NIL:
            LOG(ERROR) << "Do you forget to call SetXXX()?";
            return false;
    }
    CHECK(false) << "unknown redis type=" << _type;
    return false;
}

使用redis协议序列化到iobuf appender。


inline void RedisReply::Swap(RedisReply& other) {
    std::swap(_type, other._type);
    std::swap(_length, other._length);
    std::swap(_data.padding[0], other._data.padding[0]);
    std::swap(_data.padding[1], other._data.padding[1]);
    std::swap(_arena, other._arena);
}

交换内部字段与另一个响应的字段。


inline void RedisReply::Reset() {
    _type = REDIS_REPLY_NIL;
    _length = 0;
    _data.array.last_index = -1;
    _data.array.replies = NULL;
}
  • 重置到这个响应刚刚被构造的状态。
  • _arena不会被重置,因为要进行进一步的内存分配需要它。

void RedisStringPrinter::Print(std::ostream& os) const {
    size_t flush_start = 0;
    for (size_t i = 0; i < _str.size(); ++i) {
        const char c = _str[i];
        if (c <= 0) { 
            if (i != flush_start) {
                os << butil::StringPiece(_str.data() + flush_start, i - flush_start);
            }
            char buf[8] = "\\u0000";
            uint8_t d1 = ((uint8_t)c) & 0xF;
            uint8_t d2 = ((uint8_t)c) >> 4;
            buf[4] = (d1 < 10 ? d1 + '0' : (d1 - 10) + 'A');
            buf[5] = (d2 < 10 ? d2 + '0' : (d2 - 10) + 'A');
            os << butil::StringPiece(buf, 6);
            flush_start = i + 1;
        } else if (c == '"' || c == '\\') {
            if (i != flush_start) {
                os << butil::StringPiece(_str.data() + flush_start, i - flush_start);
            }
            os << '\\' << c;
            flush_start = i + 1;
        }
    }
    if (flush_start != _str.size()) {
        os << butil::StringPiece(_str.data() + flush_start, _str.size() - flush_start);
    }
}
  • if (c <= 0) {}else{}当c <= 0则输出c蕴含的字符,否则则需要跳过输出c。
  • 打印字段到ostream。

void RedisReply::CopyFromDifferentArena(const RedisReply& other) {
    _type = other._type;
    _length = other._length;
    switch (_type) {
    case REDIS_REPLY_ARRAY: {
        RedisReply* subs = (RedisReply*)_arena->allocate(sizeof(RedisReply) * _length);
        if (subs == NULL) {
            LOG(FATAL) << "Fail to allocate RedisReply[" << _length << "]";
            return;
        }
        for (int i = 0; i < _length; ++i) {
            new (&subs[i]) RedisReply(_arena);
        }
        _data.array.last_index = other._data.array.last_index;
        if (_data.array.last_index > 0) {
            // incomplete state
            for (int i = 0; i < _data.array.last_index; ++i) {
                subs[i].CopyFromDifferentArena(other._data.array.replies[i]);
            }
        } else {
            for (int i = 0; i < _length; ++i) {
                subs[i].CopyFromDifferentArena(other._data.array.replies[i]);
            }
        }
        _data.array.replies = subs;
    }
        break;
    case REDIS_REPLY_INTEGER:
        _data.integer = other._data.integer;
        break;
    case REDIS_REPLY_NIL:
        break;
    case REDIS_REPLY_STRING:
        // fall through
    case REDIS_REPLY_ERROR:
        // fall through
    case REDIS_REPLY_STATUS:
        if (_length < (int)sizeof(_data.short_str)) {
            memcpy(_data.short_str, other._data.short_str, _length + 1);
        } else {
            char* d = (char*)_arena->allocate((_length/8 + 1)*8);
            if (d == NULL) {
                LOG(FATAL) << "Fail to allocate string[" << _length << "]";
                return;
            }
            memcpy(d, other._data.long_str, _length + 1);
            _data.long_str = d;
        }
        break;
    }
}

从’ _arena’上分配的另一个回复复制,这是一个深度复制。


inline void RedisReply::CopyFromSameArena(const RedisReply& other) {
    _type = other._type;
    _length = other._length;
    _data.padding[0] = other._data.padding[0];
    _data.padding[1] = other._data.padding[1];
    _arena = other._arena;
}

从同一个Arena上分配的另一个回复复制,这是一个浅拷贝。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值