使用 hiredis 客户端库封装一个简单的 Redis 类

目录

思考一下redis编程的整个过程。 我们作为redis客户端。需要跟redis服务器交互。

封装 Redis 的 C++ 类的过程可以分为以下几个步骤:

一个完成发布订阅功能的 Redis 类


思考一下redis编程的整个过程。 我们作为redis客户端。需要跟redis服务器交互。

那说白了还是服务器应用开发的那一套逻辑。

1. 建立连接

2. 通信交互 (命令执行command, 服务器响应reply) 不要忘记资源回收

3. 销毁连接


 

封装 Redis 的 C++ 类的过程可以分为以下几个步骤:

定义 Redis 类:

首先,你需要定义一个 C++ 类来表示 Redis 连接和操作。

这个类通常包含连接 Redis 服务器、执行命令、处理返回结果等功能。

一个完成发布订阅功能的 Redis 类

成员变量:

Redis 服务器地址和端口:用于连接 Redis 服务器。

Redis 连接上下文:用于管理与 Redis 服务器的连接。

订阅消息处理函数:用于处理接收到的订阅消息。

功能函数:

连接函数:用于与 Redis 服务器建立连接。

订阅函数:用于向 Redis 订阅一个或多个频道,并设置订阅消息处理函数。

取消订阅函数:用于取消订阅一个或多个频道。

发布函数:用于向指定频道发布消息。

  • redisContext

管理与Redis服务器连接的上下文对象, 包含fd、flags、tcp连接信息等参数

redisContext 对象是 hiredis 库中与 Redis 服务器通信的核心对象,

它封装了连接的状态信息以及与服务器通信的相关参数。

在使用 hiredis 库时,需要先创建一个 redisContext 对象,

并使用它来执行 Redis 命令,当操作完成后,需要手动释放该对象占用的资源

typedef struct redisContext {

    int err;            // 错误码,0 表示没有错误

    char errstr[128];   // 错误信息字符串

    int fd;             // 与 Redis 服务器的连接套接字描述符

    int flags;          // 连接标志

    struct timeval timeout;   // 超时时间

    struct sockaddr_in *tcp;  // TCP 连接信息

} redisContext;
  • redisReply

存储 Redis 服务器响应的数据

当使用 hiredis 库中的 redisReply 结构体时,

它表示了 Redis 服务器的响应,包含了响应的类型、整数值、字符串值、数组元素等信息。

在使用完毕后,需要调用 freeReplyObject() 函数释放内存,以避免内存泄漏。

typedef struct redisReply {

    int type;             // Redis 响应类型,如字符串、整数、数组等

    long long integer;    // 整数值,仅在 type 为 REDIS_REPLY_INTEGER 时有效

    size_t len;           // 字符串长度或数组元素个数

    char *str;            // 字符串值,仅在 type 为 REDIS_REPLY_STRING、REDIS_REPLY_STATUS 和 REDIS_REPLY_ERROR 时有效

    size_t elements;      // 数组元素个数,仅在 type 为 REDIS_REPLY_ARRAY 时有效

    struct redisReply **element;   // 数组元素指针数组,仅在 type 为 REDIS_REPLY_ARRAY 时有效

} redisReply;
redisContext *redisConnect(const char *ip, int port);

redisConnect返回值都是靠redisContext连接上下文来接收的 eg: m_publish_context, m_subscribe_context,之后就通过操作redisContext对象来完成与Redis服务器的交互过程。redisContext相当于是Redis服务器的句柄

void *redisCommand(redisContext *c, const char *format, ...);

redisCommand 专门用来执行redis命令的

redisReply: This is the reply object returned by redisCommand() 

freeReplyObject(reply); 执行完之后需要将reply释放掉, 避免内存泄漏

redisAppendCommand 就像是把命令写在一张待发的清单上,然后继续去做其他事情,

而 redisBufferWrite 就是把这张清单上的所有命令一起发出去,

然后等待所有命令执行完毕,并把结果收集起来。

使用这两个函数结合起来的方式,可以提高程序执行 Redis 命令的效率,

特别是在需要执行大量命令或者需要异步执行命令的情况下。

  1. redisAppendCommand

    • redisAppendCommand 函数用于向 Redis 服务器发送一条 Redis 命令,但不立即等待命令执行结果。相反,它会将命令放入 hiredis 内部的输出缓冲区中,并立即返回。这使得程序可以继续执行其他操作,而不必等待命令执行完成。
    • 使用 redisAppendCommand 可以实现异步执行 Redis 命令的功能,适用于需要同时执行多个命令或者与其他操作并行执行的场景。
  2. redisBufferWrite

    • redisBufferWrite 函数用于将输出缓冲区中的 Redis 命令发送到 Redis 服务器,并等待服务器的响应。它会阻塞当前线程,直到所有待发送的命令都已经发送完成,并且等待所有命令的执行结果返回。
    • 当调用 redisBufferWrite 函数时,hiredis 库会将输出缓冲区中的所有待发送命令一次性发送到 Redis 服务器。然后,它会等待所有命令执行完成,并将执行结果存储在相应的回复对象中,以便程序后续处理。
#ifndef REDIS_H
#define REDIS_H

#include <hiredis/hiredis.h>
#include <thread>
#include <functional>
using namespace std;


class Redis
{
public:
    Redis();
    ~Redis();

    // 连接redis服务器 
    bool connect();

    // 向redis指定的通道channel发布消息
    bool publish(int channel, string message);

    // 向redis指定的通道subscribe订阅消息
    bool subscribe(int channel);

    // 向redis指定的通道unsubscribe取消订阅消息
    bool unsubscribe(int channel);

    // 在独立线程中接收订阅通道中的消息
    void observer_channel_message();

    // 初始化向业务层上报通道消息的回调对象
    void init_notify_handler(function<void(int, string)> fn);

private:
    // hiredis同步上下文对象,负责publish消息
    redisContext *_publish_context;

    // hiredis同步上下文对象,负责subscribe消息
    redisContext *_subcribe_context;

    // 回调操作,收到订阅的消息,给service层上报
    function<void(int, string)> _notify_message_handler;
};

#endif
class Redis { // 定义redis类
public:
    Redis(const char* host, int port) 
        : m_host(host), m_port(port)
        , m_publish_context(NULL)
        , m_subscribe_context(NULL)
        , m_subscribeCallback(NULL) 
    {
        connect();
    }

    ~Redis() {
        disconnect();
    }

    bool connect();      // 对于redisConnect的接口封装, 返回消息上下文.
    
    void disconnect() {  // 释放连接, 归还redis服务器系统资源             
        if (m_context) {
            redisFree(m_context);
            m_context = NULL;
        }
    }

    // 向redis指定的通道channel发布消息
    bool publish(int channel, std::string message);{
        // reply 拿到 redisCommand在Redis服务器上的执行结果/返回值/响应
        redisReply *reply = 
        (redisReply *)redisCommand(m_publish_context, "PUBLISH %d %s", channel, message.c_str());
        
        if (nullptr == reply) 做出出错处理;

        freeReplyObject(reply);
    }

    // 向redis指定的通道subscribe订阅消息
    bool subscribe(int channel) {
        if (REDIS_ERR == redisAppendCommand(m_subcribe_context, "SUBSCRIBE %d", channel)) {
            std::cerr << "subscribe command failed" << std::endl;
            return false;
        }

        // redisBufferWrite可以循环发送缓冲区, 直到缓冲区数据发送完毕(done被置为1)
        int done = 0;
        while (!done) {
            if (REDIS_ERR == redisBufferWrite(m_subcribe_context, &done)) {
                std::cerr << "subscribe command failed" << std::endl;
                return false;
            }
        }

        // redisGetReply
        return true;
    }

    // 取消订阅消息, 和subscribe函数实现几乎一毛一样, 除了命令变成UNSUBSCRIBE
    bool Redis::unsubscribe(int channel); 

    // 初始化向业务层上报通道消息的回调对象
    // set_notify_message_handler
    void init_notify_handler(function<void(int, string)> fn);

    // 在独立线程中接收订阅通道中的消息
    void Redis::observer_channel_message()
    {
        redisReply *reply = nullptr;
        while (REDIS_OK == redisGetReply(m_subcribe_context, (void **)&reply))
        {
            // 订阅收到的消息是一个带三元素的数组
            if (reply != nullptr && reply->element[2] != nullptr && reply->element[2]->str != nullptr)
            {
                // 给业务层上报通道上发生的消息
                _notify_message_handler(atoi(reply->element[1]->str) , reply->element[2]->str);
            }

            freeReplyObject(reply);
        }

        cerr << ">>>>>>>>>>>>> observer_channel_message quit <<<<<<<<<<<<<" << endl;
    }


private:
    // ip + port

    // hiredis同步上下文对象, 负责publish消息
    redisContext *m_publish_context;         // 发布消息上下文

    // hiredis同步上下文对象, 负责subscribe消息 
    redisContext *m_subcribe_context;        // 订阅消息上下文

    // 回调操作, 收到订阅的消息, 给service层上报, 订阅消息处理回调函数
    function<void(int, string)> m_notify_message_handler;
};
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杰312

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值