Redis 驱动适配 Reactor 模式

文章目录

    • 一、核心目标与事件体系构建
      • [1.1 构建两类核心事件对象](#1.1 构建两类核心事件对象)
      • [1.2 适配 Reactor 事件循环](#1.2 适配 Reactor 事件循环)
      • [1.3 hiredis 封装规则与适配要点](#1.3 hiredis 封装规则与适配要点)
    • [二、Redis 异步连接与实现方案](#二、Redis 异步连接与实现方案)
      • [2.1 同步与异步连接的核心差异](#2.1 同步与异步连接的核心差异)
      • [2.2 异步连接的协议界定](#2.2 异步连接的协议界定)
      • [2.3 基于 hiredis + libevent 的实现方案](#2.3 基于 hiredis + libevent 的实现方案)
        • [2.3.1 核心结构体:redisContext](#2.3.1 核心结构体:redisContext)
        • [2.3.2 事件绑定函数:redisLibeventAttach](#2.3.2 事件绑定函数:redisLibeventAttach)
      • [2.4 Reactor 处理 Redis 异步连接](#2.4 Reactor 处理 Redis 异步连接)
    • [三、Reactor 网络模型与 Redis 驱动的适配细节](#三、Reactor 网络模型与 Redis 驱动的适配细节)
      • [3.1 Reactor 核心事件结构体定义](#3.1 Reactor 核心事件结构体定义)
      • [3.2 Redis 事件与 Reactor 事件的适配设计(C 语言"继承")](#3.2 Redis 事件与 Reactor 事件的适配设计(C 语言“继承”))
      • [3.3 Reactor 事件注册与循环的核心代码解析](#3.3 Reactor 事件注册与循环的核心代码解析)
        • [3.3.1 事件注册函数(add_event)](#3.3.1 事件注册函数(add_event))
        • [3.3.2 事件循环函数(eventloop_once)](#3.3.2 事件循环函数(eventloop_once))
        • [3.3.3 Redis 事件更新函数(redisEventUpdate)](#3.3.3 Redis 事件更新函数(redisEventUpdate))
      • [3.4 hiredis 事件接口与 Reactor 函数的适配](#3.4 hiredis 事件接口与 Reactor 函数的适配)
    • [四、自定义 Redis 协议实现](#四、自定义 Redis 协议实现)
      • [4.1 自定义协议解压缩(解析 Redis 响应)](#4.1 自定义协议解压缩(解析 Redis 响应))
        • [4.1.1 读取协议行:readline](#4.1.1 读取协议行:readline)
        • [4.1.2 核心解析函数:read_sub_response](#4.1.2 核心解析函数:read_sub_response)
        • [4.1.3 响应读取入口:read_response](#4.1.3 响应读取入口:read_response)
      • [4.2 自定义协议压缩(构造 Redis 请求)](#4.2 自定义协议压缩(构造 Redis 请求))
        • [4.2.1 写入数组元素个数:write_count](#4.2.1 写入数组元素个数:write_count)
        • [4.2.2 写入字符串类型头:write_header](#4.2.2 写入字符串类型头:write_header)
        • [4.2.3 写入 Redis 命令:write_command](#4.2.3 写入 Redis 命令:write_command)
        • [4.2.4 请求构造入口:RunCommand](#4.2.4 请求构造入口:RunCommand)

一、核心目标与事件体系构建

核心目标是将 Redis 连接管理与 Reactor 事件驱动模式 融合,利用Reactor的高效事件调度来处理Redis连接的事件(如网络IO、命令响应等)。

我们有两种事件对象:hiredis事件对象和Reactor事件对象,需要将它们进行关联。为了避免重复造轮子,我们适配项目已有的 Reactor 事件循环,让Redis的事件能在Reactor中被调度。

hiredis 提供了灵活的接口,允许自行实现IO操作和事件操作接口,我们需要将这些接口与项目的事件处理机制适配。

1.1 构建两类核心事件对象

为让 Redis 事件融入 Reactor 体系,需定义并关联两类事件对象,确保事件能被 Reactor 识别和调度:

  • hiredis 事件对象 :由 Redis 客户端库 hiredis 原生定义,用于描述 Redis 交互相关的事件,如连接建立、数据读写、命令响应等。
  • Reactor 事件对象 :项目中 Reactor 模式使用的事件结构体,需与 hiredis 事件对象做关联或适配(如通过指针绑定、字段嵌套),使 Redis 事件能接入 Reactor 的事件循环流程。

1.2 适配 Reactor 事件循环

为了避免重复开发事件循环逻辑,Redis 驱动需要适配项目已有的 Reactor 事件循环。具体通过实现 hiredis 要求的事件操作接口来完成:

  • addRead:注册读事件到 Reactor
  • delRead:从 Reactor 注销读事件
  • addWrite:注册写事件到 Reactor
  • delWrite:从 Reactor 注销写事件
  • cleanup:清理事件资源
  • scheduleTimer:设置超时定时器

1.3 hiredis 封装规则与适配要点

hiredis 对事件与 IO 的封装具有很好的灵活性,主要体现在:

IO 自主实现支持hiredis 不强制依赖内部 IO 逻辑,允许使用者根据项目需求自定义 IO 操作(如连接建立、数据收发),只需适配其提供的接口即可。

事件操作接口适配hiredis 提供统一的事件操作接口(如事件注册、删除、触发、定时器调度),适配时需将这些接口与项目 Reactor 的事件接口对齐(如把 hiredisaddRead 映射到 Reactor 的 add_event),让 Reactor 接管 hiredis 的事件生命周期。

多库/多平台兼容 :不同网络库(如 libeventlibuv)、不同操作系统对事件操作的接口定义(函数签名、参数格式)存在差异,适配时需针对这些差异做兼容处理,确保 Redis 驱动在不同环境下都能与事件系统协同工作。


二、Redis 异步连接与实现方案

Redis 连接分为同步与异步两种模式,异步模式基于非阻塞 IO 实现,是适配 Reactor 模式的核心方向,以下详细说明其原理、实现与选择依据。

2.1 同步与异步连接的核心差异

Redis 同步连接采用阻塞IO,当发送命令后,当前线程会阻塞直至Redis返回结果。优点是代码书写是同步的,业务逻辑不会被割裂,易于理解和维护。缺点是阻塞当前线程,在高并发场景下效率较低;通常需要通过多线程池来提升处理能力。

Redis 异步连接采用非阻塞 IO 实现,其优点是不会阻塞当前线程,即使 Redis 未返回结果,仍可继续向 Redis 发送其他命令,并发效率更高;但缺点是代码需通过回调函数实现,易导致业务逻辑割裂,该问题可通过协程(如 openresty、skynet)解决。此外,异步连接配合 Redis 6.0 及以上版本的 IO 多线程(需大量并发请求场景)、异步连接池,能进一步提升应用层的数据访问性能。

2.2 异步连接的协议界定

实现 Redis 协议的第一步是明确数据包的界定方式,主要有两种:

  1. 长度 + 二进制流:先传输数据的长度信息,再传输对应的二进制数据,通过长度确定数据包边界,避免粘包问题。
  2. 二进制流 + 特殊分隔符 :通过特定字符(如 \r\n)作为数据包的结束标识,解析时通过识别分隔符确定数据包边界,Redis 原生协议即采用此方式。

2.3 基于 hiredis + libevent 的实现方案

hiredis 可与 libevent(轻量级事件库)结合实现 Redis 异步连接,核心依赖 hiredis 的异步上下文结构体与事件绑定函数,以下为关键代码与解析:

2.3.1 核心结构体:redisContext

redisContexthiredis 中描述 Redis 连接上下文的核心结构体,存储连接的 IO 句柄、错误信息、读写缓冲区、协议解析器等关键信息,部分字段含义如下:

c 复制代码
typedef struct redisContext {
    const redisContextFuncs *funcs;   // 函数表,存储事件操作函数指针
    int err;                          // 错误标志,0 表示无错误
    char errstr[128];                 // 错误信息字符串
    redisFD fd;                       // 连接的文件描述符(socket)
    char *obuf;                       // 写缓冲区,存储待发送给 Redis 的数据
    redisReader *reader;              // 协议解析器,处理 Redis 响应数据
    enum redisConnectionType connection_type; // 连接类型(TCP/UNIX 域套接字)
    void *privdata;                   // 用户自定义私有数据,hiredis 不直接使用
    void (*free_privdata)(void *);    // 私有数据的释放回调函数
} redisContext;
2.3.2 事件绑定函数:redisLibeventAttach

该函数用于将 hiredis 的异步上下文(redisAsyncContext)与 libevent 的事件底座(event_base)绑定,实现 hiredis 事件与 libevent 事件循环的对接,关键逻辑如下:

c 复制代码
static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
    redisContext *c = &(ac->c);
    redisLibeventEvents *e; // 存储 hiredis 与 libevent 事件关联的容器
    
    // 若已绑定事件底座,直接返回错误
    if (ac->ev.data != NULL) return REDIS_ERR;
    
    // 分配事件容器内存
    e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
    if (e == NULL) return REDIS_ERR;
    
    e->context = ac; // 绑定 hiredis 异步上下文
    e->base = base;  // 绑定 libevent 事件底座
    
    // 替换 hiredis 的事件操作接口,让 libevent 接管事件
    ac->ev.addRead = redisLibeventAddRead;    // 注册读事件
    ac->ev.delRead = redisLibeventDelRead;    // 注销读事件
    ac->ev.addWrite = redisLibeventAddWrite;  // 注册写事件
    ac->ev.delWrite = redisLibeventDelWrite;  // 注销写事件
    ac->ev.cleanup = redisLibeventCleanup;    // 事件清理
    ac->ev.scheduleTimer = redisLibeventSetTimeout; // 定时器调度
    ac->ev.data = e; // 存储事件容器,供后续操作使用
    
    // 创建 libevent 事件(监听读/写事件),绑定事件处理函数 redisLibeventHandler
    e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);
    return REDIS_OK;
}

2.4 Reactor 处理 Redis 异步连接

eactor 模式通过事件驱动机制处理 Redis 连接的建立、数据读写等过程,具体步骤如下:

  1. 创建非阻塞 Socket 并发起连接 :先创建 Socket 并设置为非阻塞模式,调用 connect 后会立刻返回(无论连接是否成功),避免线程阻塞。
  2. 通过写事件确认连接建立 :连接建立前,Reactor 需为该 Socket 注册写事件;当写事件触发时,说明 Redis 连接已建立(底层三次握手完成)。
  3. 注销写事件并注册读事件 :连接建立后必须注销写事件。
    原因是:
    • 连接建立前,epoll 检测的是三次握手的同步包;
    • 建立后,epoll 会转为检测 Socket 的发送缓冲区
    • 如果不注销,写事件会因发送缓冲区非满而持续触发,导致资源浪费。
    • 注销后注册读事件,用于监听 Redis 的响应数据。
  4. 数据发送与事件调整:向 Redis 发送命令时,若发送失败或数据未发送完整(如发送缓冲区已满),需重新注册写事件;待下次写事件触发(发送缓冲区有空间)时,继续发送剩余数据。
  5. 响应数据处理 :当 Redis 返回响应数据时,读事件触发,Reactor 调用对应的读事件处理函数,通过 hiredis 的协议解析器解析数据,完成业务逻辑。

三、Reactor 网络模型与 Redis 驱动的适配细节

Reactor 网络模型是 Redis 异步驱动的核心底座,需通过事件结构体设计、接口适配、事件操作函数实现,确保 Redis 事件能被 Reactor 正确调度。

3.1 Reactor 核心事件结构体定义

Reactor 模式通过 event_t 结构体描述一个事件,包含事件关联的文件描述符、Reactor 实例、缓冲区、回调函数等,是 Reactor 识别事件的基础,定义如下:

c 复制代码
struct event_s {
    int fd;                          // 事件关联的文件描述符(如 Redis 连接的 Socket)
    reactor_t *r;                    // 关联的 Reactor 实例
    buffer_t *in;                    // 读缓冲区,存储从 Socket 读取的数据
    buffer_t *out;                   // 写缓冲区,存储待写入 Socket 的数据
    event_callback_fn read_fn;       // 读事件回调函数(事件触发时执行)
    event_callback_fn write_fn;      // 写事件回调函数
    error_callback_fn error_fn;      // 错误事件回调函数
};

3.2 Redis 事件与 Reactor 事件的适配设计(C 语言"继承")

Reactor 仅能识别 event_t 结构体,而 Redis 驱动需关联 hiredis 的异步上下文(redisAsyncContext),因此通过 结构体字段嵌套 实现"继承"效果(C 语言中,结构体起始地址相同可强转),定义 redis_event_t 结构体如下:

c 复制代码
typedef struct {
    event_t e;                       // 嵌套 Reactor 事件结构体,作为起始字段
    int mask;                        // 事件掩码(如 EPOLLIN、EPOLLOUT)
    redisAsyncContext *ctx;          // 关联的 hiredis 异步上下文
} redis_event_t;

适配逻辑:创建 redis_event_t 实例后,取其 e 字段(event_t 类型)注册到 Reactor;当 Reactor 触发事件并返回 event_t* 时,可将其强转为 redis_event_t*,从而访问 ctx 字段操作 Redis 连接,实现 Reactor 事件与 Redis 上下文的绑定。

3.3 Reactor 事件注册与循环的核心代码解析

Reactor 通过 epoll 实现事件检测,核心包含事件注册(add_event)、事件循环(eventloop_once)、事件更新(redisEventUpdate)等函数,确保 Redis 事件能被正确调度。

3.3.1 事件注册函数(add_event)

该函数将 event_t 注册到 Reactor 的 epoll 实例,关联事件掩码(如读/写事件),代码如下:

c 复制代码
int add_event(reactor_t *R, int events, event_t *e) {
    struct epoll_event ev;
    ev.events = events;              // 设置事件类型(如 EPOLLIN | EPOLLOUT)
    ev.data.ptr = e;                 // 绑定事件结构体,供 epoll_wait 返回时使用
    // 调用 epoll_ctl 注册事件,EPOLL_CTL_ADD 表示新增事件
    if (epoll_ctl(R->epfd, EPOLL_CTL_ADD, e->fd, &ev) == -1) {
        printf("add event err fd = %d\n", e->fd);
        return 1;
    }
    return 0;
}
3.3.2 事件循环函数(eventloop_once)

该函数是 Reactor 的核心,通过 epoll_wait 等待事件触发,再根据事件类型调用对应的回调函数,代码如下:

c 复制代码
void eventloop_once(reactor_t * r, int timeout) {
    // 等待事件触发,timeout 为超时时间(毫秒),返回触发的事件数
    int n = epoll_wait(r->epfd, r->fire, MAX_EVENT_NUM, timeout);
    for (int i = 0; i < n; i++) {
        struct epoll_event *e = &r->fire[i];
        int mask = e->events;
        // 若触发错误或挂断事件,同时标记读/写事件(确保后续处理)
        if (e->events & EPOLLERR) mask |= EPOLLIN | EPOLLOUT;
        if (e->events & EPOLLHUP) mask |= EPOLLIN | EPOLLOUT;
        
        // 将 epoll 返回的事件数据强转为 event_t*
        event_t *et = (event_t*) e->data.ptr;
        // 触发读事件,调用读回调函数
        if (mask & EPOLLIN && et->read_fn) {
            et->read_fn(et->fd, EPOLLIN, et);
        }
        // 触发写事件,调用写回调函数;若无回调,直接从缓冲区写数据
        if (mask & EPOLLOUT) {
            if (et->write_fn) {
                et->write_fn(et->fd, EPOLLOUT, et);
            } else {
                uint8_t * buf = buffer_write_atmost(evbuf_out(et));
                event_buffer_write(et, buf, buffer_len(evbuf_out(et)));
            }
        }
    }
}
3.3.3 Redis 事件更新函数(redisEventUpdate)

该函数通过位运算调整 redis_event_t 的事件掩码,动态注册/注销 Reactor 事件(如添加读事件、删除写事件),代码逻辑如下:

c 复制代码
static void redisEventUpdate(void *privdata, int flag, int remove) {
    redis_event_t *re = (redis_event_t *)privdata; // 强转为 Redis 事件结构体
    reactor_t *r = re->e.r;
    int prevMask = re->mask; // 调整前的事件掩码
    int enable = 0;

    // 处理事件注销:从掩码中移除目标事件(如 EPOLLOUT)
    if (remove) {
        if ((re->mask & flag) == 0) return; // 若未注册该事件,直接返回
        re->mask &= ~flag;
        enable = 0;
    } 
    // 处理事件注册:向掩码中添加目标事件(如 EPOLLIN)
    else {
        if (re->mask & flag) return; // 若已注册该事件,直接返回
        re->mask |= flag;
        enable = 1;
    }

    int fd = re->ctx->c.fd;
    // 若事件掩码为空,删除该事件(从 epoll 中移除)
    if (re->mask == 0) {
        del_event(r, &re->e);
    } 
    // 若之前无事件,新增事件(注册到 epoll)
    else if (prevMask == 0) {
        add_event(r, re->mask, &re->e);
    } 
    // 若已有事件,更新事件状态(启用/禁用读/写事件)
    else {
        if (flag & EPOLLIN) {
            enable_event(r, &re->e, enable, 0); // 更新读事件
        } else if (flag & EPOLLOUT) {
            enable_event(r, &re->e, 0, enable); // 更新写事件
        }
    }
}

3.4 hiredis 事件接口与 Reactor 函数的适配

hiredis 的异步上下文(redisAsyncContext)通过 ev 字段存储事件操作函数,适配时需将这些函数映射到 Reactor 的事件操作(add_eventdel_event 等),核心适配代码如下:

c 复制代码
static int redisAttach(reactor_t *r, redisAsyncContext *ac) {
    redisContext *c = &(ac->c);
    redis_event_t *re;

    // 若已绑定 Reactor,直接返回错误
    if (ac->ev.data != NULL) return REDIS_ERR;

    // 分配 Redis 事件结构体内存
    re = (redis_event_t*)hi_malloc(sizeof(*re));
    if (re == NULL) return REDIS_ERR;

    // 绑定 hiredis 上下文与 Reactor 实例
    re->ctx = ac;
    re->e.fd = c->fd;
    re->e.r = r;
    re->e.in = NULL; // 不使用 Reactor 缓冲区,复用 hiredis 自身缓冲区
    re->e.out = NULL;
    re->mask = 0;

    // 将 hiredis 事件接口映射到 Reactor 操作(关键适配步骤)
    ac->ev.addRead = redisAddRead;    // 读事件注册 → 调用 redisEventUpdate 实现
    ac->ev.delRead = redisDelRead;    // 读事件注销 → 调用 redisEventUpdate 实现
    ac->ev.addWrite = redisAddWrite;  // 写事件注册 → 调用 redisEventUpdate 实现
    ac->ev.delWrite = redisDelWrite;  // 写事件注销 → 调用 redisEventUpdate 实现
    ac->ev.cleanup = redisCleanup;    // 事件清理 → 释放 redis_event_t 内存
    ac->ev.data = re; // 存储 Redis 事件结构体,供后续操作使用

    return REDIS_OK;
}

适配后,hiredis 触发的读/写事件操作,会间接调用 Reactor 的 add_eventdel_event 等函数,实现 Redis 事件与 Reactor 循环的深度整合。


四、自定义 Redis 协议实现

当需要让 Redis 驱动与项目自定义数据结构(如类似 Lua Table 的 SVar)契合时,可跳过 hiredis 的协议解析,自行实现 Redis 协议的解压缩(解析响应)与压缩(构造请求),确保数据格式与项目兼容。

4.1 自定义协议解压缩(解析 Redis 响应)

Redis 响应协议以不同字符开头标识数据类型(如 $ 表示二进制安全字符串、* 表示数组),read_sub_response 函数通过递归解析不同类型的响应,将结果存入项目自定义的 SVar 类型,核心代码与逻辑如下:

4.1.1 读取协议行:readline

从缓冲区中读取以 \r\n 结尾的一行数据,确定协议行的长度,代码如下:

c 复制代码
static bool readline(u_char *start, u_char *last, int &pos) {
    // 遍历缓冲区,查找 '\r' 且下一个字符为 '\n' 的位置
    for (pos = 0; start+pos <= last-1; pos++) {
        if (start[pos] == '\r' && start[pos+1] == '\n') {
            pos--; // pos 指向 '\r' 前的最后一个有效字符
            return true;
        }
    }
    return false; // 未找到完整的 '\r\n',数据不完整
}
4.1.2 核心解析函数:read_sub_response

递归解析 Redis 响应的不同类型,返回解析状态(-2 错误、-1 数据不完整、0 成功、1 错误信息),代码逻辑如下:

c 复制代码
static int read_sub_response(u_char *start, u_char *last, SVar &s, int &usz) {
    int pos = 0;
    // 先读取一行协议头,若读取失败(数据不完整),返回 -1
    if (!readline(start, last, pos)) return -1;
    
    u_char *tail = start + pos + 1; // 指向 '\r' 的位置
    u_char ch = start[0]; // 协议类型标识(如 $、+、-、:、*)
    // usz 累计已解析的字节数(协议头长度:pos+2+1 = 有效字符数 + '\r\n' 长度)
    usz += pos + 2 + 1;

    switch (ch) {
        // 类型 1:$ 表示二进制安全字符串(如普通响应数据)
        case '$': {
            // 读取字符串长度(协议头中 '#' 后的数字)
            string str(start+1, tail);
            int len = atoi(str.c_str());
            if (len < 0) return 0; // len <0 表示 nil(空值)
            // 若缓冲区剩余长度不足字符串长度 + '\r\n',返回数据不完整
            if (tail + 2 + len > last) return -1;
            // 将字符串数据存入 SVar
            s = string(tail+2, tail+2+len);
            usz += len + 2; // 累计字符串长度 + '\r\n' 长度
            return 0;
        }
        // 类型 2:+ 表示简单字符串(如 "OK")
        case '+': {
            s = string(start+1, tail); // 直接读取协议头后的字符串
            return 0;
        }
        // 类型 3:- 表示错误信息(如 "ERR wrong number of arguments")
        case '-': {
            s = string(start+1, tail); // 读取错误信息存入 SVar
            return 1; // 返回 1 标识为错误信息
        }
        // 类型 4:: 表示整数(如计数、状态码)
        case ':': {
            string str(start+1, tail); // 读取数字字符串并转为浮点数(兼容整数)
            s = atof(str.c_str());
            return 0;
        }
        // 类型 5:* 表示数组(如命令返回的多元素结果)
        case '*': {
            // 读取数组元素个数
            string str(start+1, tail);
            int n = atoi(str.c_str());
            if (n == 0) return 0; // n=0 表示空数组
            if (n < 0) return 0; // n<0 表示超时或空值

            int ok = 0;
            u_char *pt = tail + 2; // 指向数组第一个元素的起始位置
            for (int i = 0; i < n; i++) {
                if (pt > last) return -1; // 缓冲区不足,返回数据不完整
                int sz = 0;
                SVar t; // 存储单个元素
                // 递归解析数组中的每个元素
                int ret = read_sub_response(pt, last, t, sz);
                if (ret < 0) return -1; // 解析失败,返回错误
                s.Insert(t); // 将元素插入 SVar 数组
                usz += sz; // 累计元素解析的字节数
                pt += sz; // 移动指针到下一个元素
                if (ret == 1) ok = 1; // 若有元素是错误信息,标记 ok=1
            }
            return ok;
        }
    }
    return -2; // 未知协议类型,返回解析错误
}
4.1.3 响应读取入口:read_response

从项目自定义的句柄(SHandle)缓冲区中读取数据,调用 read_sub_response 解析,代码如下:

c 复制代码
static int read_response(SHandle *pHandle, SVar &s, int &size) {
    int len = pHandle->GetCurBufSize(); // 获取缓冲区当前数据长度
    u_char *start = pHandle->m_pBuffer; // 缓冲区起始地址
    u_char *last = pHandle->m_pBuffer + len; // 缓冲区末尾地址
    // 调用核心解析函数,返回解析状态
    return read_sub_response(start, last, s, size);
}

4.2 自定义协议压缩(构造 Redis 请求)

构造 Redis 请求需遵循 Redis 协议格式(数组类型为主,如 *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n),通过 write_countwrite_headerwrite_command 等函数拼接请求字符串,核心代码如下:

4.2.1 写入数组元素个数:write_count

向请求字符串中写入数组类型标识(*)和元素个数,代码如下:

c 复制代码
static void write_count(string &req, size_t n) {
    char chv[16] = {0};
    _itoa(n, chv, 10); // 将元素个数转为字符串
    req.append("*"); // 写入数组标识
    req.append(chv); // 写入元素个数
}
4.2.2 写入字符串类型头:write_header

向请求字符串中写入字符串类型标识($)、字符串长度和 \r\n,代码如下:

c 复制代码
static void write_header(string &req, size_t n) {
    char chv[16] = {0};
    _itoa(n, chv, 10); // 将字符串长度转为字符串
    req.append("\r\n$"); // 写入换行和字符串标识
    req.append(chv); // 写入字符串长度
    req.append("\r\n"); // 写入换行
}
4.2.3 写入 Redis 命令:write_command

向请求字符串中写入 Redis 命令(如 SET),并拼接对应的字符串头,代码如下:

c 复制代码
static void write_command(string &req, const char *cmd) {
    int n = strlen(cmd); // 获取命令长度
    write_header(req, n); // 写入字符串头(长度信息)
    req.append(cmd); // 写入命令内容
}
4.2.4 请求构造入口:RunCommand

根据 Redis 命令(如 SET)和参数列表(如 keyvalue),构造完整请求并发送,代码如下:

c 复制代码
void SRedisClient::RunCommand(const char* cmd, vector<string> &params) {
    string req; // 存储完整请求字符串
    size_t nsize = params.size(); // 参数个数
    // 写入数组标识和总元素个数(命令 + 参数个数)
    write_count(req, nsize + 1);
    // 写入 Redis 命令(如 SET)
    write_command(req, cmd);
    // 遍历参数列表,写入每个参数
    for (size_t i = 0; i < params.size(); i++) {
        size_t n = params[i].size(); // 参数长度
        write_header(req, n); // 写入参数的字符串头
        req.append(params[i]); // 写入参数内容
    }
    req.append("\r\n"); // 补充请求末尾的换行
    Send(req); // 发送请求到 Redis 服务器
}
相关推荐
一 乐2 分钟前
远程在线诊疗|在线诊疗|基于java和小程序的在线诊疗系统小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·小程序
缺点内向4 分钟前
Java:高效删除Excel中的空白行和列
java·开发语言·excel
Yurko1313 分钟前
【计网】基于三层交换机和 RIP 协议的局域网组建
网络·学习·计算机网络·智能路由器
静若繁花_jingjing16 分钟前
DDD领域驱动设计实践_保险
java·开发语言
无序的浪18 分钟前
网络初识~
网络
程序猿202319 分钟前
Python每日一练---第十二天:验证回文串
开发语言·python
wjs202422 分钟前
AJAX 实例详解
开发语言
我要升天!24 分钟前
QT -- 初识
开发语言·qt
wjs202439 分钟前
Memcached flush_all 命令详解
开发语言
落叶的悲哀1 小时前
mysql tidb like查询有换行符内容问题解决
数据库·mysql·tidb