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

news/2024/9/24 13:17:32/

目录

redis%E7%BC%96%E7%A8%8B%E7%9A%84%E6%95%B4%E4%B8%AA%E8%BF%87%E7%A8%8B%E3%80%82%20%E6%88%91%E4%BB%AC%E4%BD%9C%E4%B8%BAredis%E5%AE%A2%E6%88%B7%E7%AB%AF%E3%80%82%E9%9C%80%E8%A6%81%E8%B7%9Fredis%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%BA%A4%E4%BA%92%E3%80%82-toc" style="margin-left:40px;">思考一下redis编程的整个过程。 我们作为redis客户端。需要跟redis服务器交互。

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

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


redis%E7%BC%96%E7%A8%8B%E7%9A%84%E6%95%B4%E4%B8%AA%E8%BF%87%E7%A8%8B%E3%80%82%20%E6%88%91%E4%BB%AC%E4%BD%9C%E4%B8%BAredis%E5%AE%A2%E6%88%B7%E7%AB%AF%E3%80%82%E9%9C%80%E8%A6%81%E8%B7%9Fredis%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%BA%A4%E4%BA%92%E3%80%82">思考一下redis编程的整个过程。 我们作为redis客户端。需要跟redis服务器交互。

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

1. 建立连接

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

3. 销毁连接


 

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

定义 Redis 类:

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

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

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

成员变量:

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

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

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

功能函数:

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

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

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

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

管理与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;

存储 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;}}// redisGetReplyreturn true;}// 取消订阅消息, 和subscribe函数实现几乎一毛一样, 除了命令变成UNSUBSCRIBEbool Redis::unsubscribe(int channel); // 初始化向业务层上报通道消息的回调对象// set_notify_message_handlervoid 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;
};

http://www.ppmy.cn/news/1435042.html

相关文章

QTableView获取可见的行数

场景 当我们需要实时刷新QTableView时&#xff0c;而此时tableView的数据量较大&#xff0c;如果全部刷新显然不合理&#xff0c;如果可以只对用户看的到的数据进行刷新那就最好了&#xff0c;经过一番摸索找到了几种方式&#xff0c;可供参考 代码 方法1 QVector<int>…

Syncovery for Mac:高效文件备份和同步工具

Syncovery for Mac是一款专为Mac用户设计的文件备份和同步工具&#xff0c;凭借其高效、安全和易用的特点&#xff0c;深受用户好评。 Syncovery for Mac v10.14.2激活版下载 该软件具备强大的备份功能&#xff0c;支持多种备份方案和数据格式&#xff0c;用户可以根据需求轻松…

简单工厂、工厂方法、抽象工厂对比

简单工厂、工厂方法和抽象工厂是三种常见的工厂设计模式&#xff0c;它们在软件设计中各有其独特的应用场景和优缺点。因为三种设计模式都属于工厂模式&#xff0c;在实际应用中可能存在误用的场景&#xff0c;这里对其做下对比&#xff0c;以便更好的理解这三种设计模式。 简…

【期末复习向】智能信息系统前4章梳理

第四章 不确定性推理 [了解即可]4.1 不确定性概述 不确定性推理概念 所谓推理&#xff0c;就是从已知事实出发&#xff0c;运用相关的知识&#xff08;或规则&#xff09;逐步推出结论或者证明某个假设成立或不成立的思维过程。 不确定性方法分类 1.模型方法 a&#xff0…

5分钟搞懂词向量生成技术:Word2Vec

大家好啊&#xff0c;我是董董灿。 今天我们来简单聊一个在自然语言处理&#xff08;NLP&#xff09;中非常有用的技术——Word2Vec。 之前曾经写过一些关于文本处理的基础知识&#xff0c;包括判断两个文本是否相似&#xff0c;可以使用余弦相似度&#xff0c;但在此之前&am…

【Redis(4)】Redis主从复制模式配置示例

Redis的三种高可用方案对比参考上一篇博客&#xff1a;深入理解Redis三种高可用方案&#xff0c;以做出明智的选择&#xff0c;下面要探讨的是三种方案其中的一种。 在构建高性能的缓存系统时&#xff0c;Redis以其卓越的性能和简单性成为了首选。本文将探讨Redis主从复制的工…

基于Google Gemini 探索大语言模型在医学领域应用评估和前景

概述 近年来&#xff0c;大规模语言模型&#xff08;LLM&#xff09;在理解和生成人类语言方面取得了显著的飞跃&#xff0c;这些进步不仅推动了语言学和计算机编程的发展&#xff0c;还为多个领域带来了创新的突破。特别是模型如GPT-3和PaLM&#xff0c;它们通过吸收海量文本…

B2弹幕插件优化版WordPress插件

源码下载&#xff1a;B2弹幕插件.zip 这是b2独有的站点信息弹幕插件&#xff0c;专门用来在首页显示站点动态的一款个性化 WordPress插件。喜欢的可以下载回去进行二次开发&#xff0c;还是蛮不错的 基于wordpress 7B2主题开发的一款弹幕插件/气泡插件 功能一览 插件安装&a…