使用 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;}}// 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.mzph.cn/pingmian/1333.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux的UDEV机制

udev 机制引入&#xff1a; 手机接入Linux热拔插相关 a. 把手机接入开发板 b. 安装adb工具&#xff0c;在终端输入adb安装指令&#xff1a; sudo apt-get install adb c. dmeg能查看到手机接入的信息&#xff0c;但是输入adb devices会出现提醒 dinsufficient permissions for …

【Java】HashMap、HashTable和ConcurrentHashMap的区别

文章目录 区别一、HashMap1.1基本定义与特性1.2工作原理与实现1.3常用方法1.4性能与优化 二、HashTable三、ConcurrentHashMap3.1基本特点3.2实现原理3.3常用方法3.4适用场景3.5性能优化 HashTable、HashMap和ConcurrentHashMap之间的区别主要体现在线程安全、继承关系与实现接…

Mysql 和 PostgreSQL 到底选啥?

当我深入探讨MySQL和PostgreSQL这两个著名的开源数据库时&#xff0c;我们不仅发现它们在功能、性能和用例方面存在明显的差异&#xff0c;同时也能看出它们各自在特定场景下的独特优势。选择哪一个往往取决于项目的具体需求、团队的熟悉度以及未来的扩展计划。 在这篇文章中&…

kaggle 泰坦尼克号2 得分0.7799

流程 导入所要使用的包引入kaggle的数据集csv文件查看数据集有无空值填充这些空值提取特征分离训练集和测试集调用模型 导入需要的包 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarni…

Vue3: 获取元素DOM的方法

Vue3中获取dom的方法有两种 : ref模板引用和传统方法 1.ref模板引用 模板引用是官方提出的方法&#xff0c;请看下面的例子&#xff1a; <template><canvas ref"solarCanvas" id"solar" width"1300" height"900"></…

K8S 污点和容忍度(Taint,Toleration)

介绍 在 Kubernetes 中&#xff0c;污点&#xff08;Taints&#xff09;和容忍度&#xff08;Tolerations&#xff09;是用于节点调度的一种机制&#xff0c;它们允许你控制哪些 Pod 能够调度到哪些节点上。 污点&#xff08;Taints&#xff09; 污点是节点上的一种属性&…

从C到JAVA之学习JAVA的第一周笔记

文章目录 java语言概述JDK与JRE编写执行过程第一份java代码解读编写编译运行其他 注释三种注释方法 java API文档关键字标识符数据类型基本数据类型自动类型提升规则引用数据类型 string概述String与基本数据类型的变量间的运算 运算符键盘录入运行控制语句数组定义与静态初始化…

springboot no mapping for.....解决办法

这个问题是由于没有加入对应的GET,POST注解&#xff0c;导致映射失败&#xff0c;加入对应注解就ok了

JDK 11下载、安装、配置

下载 到Oracle管网下载JDK 11&#xff0c;下载前需要登录&#xff0c;否则直接点下载会出现502 bad gateway。 下载页面链接 https://www.oracle.com/hk/java/technologies/downloads/#java11-windows 登录 有些人可能没有Oracle账号&#xff0c;注册也比较慢&#xff0c;有需…

随笔05 我的创作纪念日(512天)

机缘 机缘这事儿&#xff0c;我在随笔系列博文里已经翻来覆去说了不少&#xff0c;这次就不再唠叨了&#xff0c;省得被小伙伴嫌弃成祥林嫂~&#x1f61c; &#x1f338;随笔01 我的创作纪念日&#xff08;128天&#xff09;_newmitbbs-CSDN博客 收获 我这一小片自留地&…

os模块学习

【一】文件路径相关的操作 【1】获取当前文件所在的文件夹路径 # os.path.dirname(__file__) ​ import os file_name os.path.dirname(__file__) print(file_name) # H:\pycharm projects\day\模块学习2 【2】获取当前文件所在的文件路径 # os.path.abspath(__fil…

echarts部分属性使用

标题部分 (title): 控制图表的标题显示&#xff0c;包括主标题和副标题。你可以设置标题的文字内容、样式、位置等属性。 图例部分 (legend): 图例是用来标识每个系列的名称的&#xff0c;可以让用户通过点击图例来控制显示/隐藏对应的数据系列。 提示框部分 (tooltip): 当鼠…

Rust基本数据类型-字符串

一、字符串是什么&#xff0c;怎么用 1、字符串是什么 先说明一下&#xff0c;在Rust中&#xff0c;字符是UniCode编码占4个字节&#xff0c;字符串类型的字符是UTF-8编码的&#xff0c;字节大小为1&#xff5e;3。 字符串类型在Rust中&#xff0c;可以分为&Str和String…

【极速前进】20240415-20240421:TR-DPO、压缩与智能的线性关系、模拟伪代码改善算术能力、Many-shot、合成数据综述

一、TR-DPO&#xff1a;更新reference模型能实现更好的对齐 论文地址&#xff1a;https://arxiv.org/pdf/2404.09656.pdf ​ 语言模型对齐的训练目标是&#xff1a; max ⁡ π θ E x ∼ D , y ∼ π θ ( y ∣ x ) [ r ϕ ( x , y ) ] − β D KL [ π θ ( x , y ) ∥ π …

JavaEE 初阶篇-深入了解 File 文件操作(实现文件搜索、非空文件夹删除)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 File 文件概述 2.0 创建 File 类对象的方法 2.1 判断文件类型、获取文件信息的方法 2.2 创建文件、删除文件的方法 2.3 遍历文件夹的方法 3.0 文件搜索与删除 3.1…

OSPF面试题收集

第一章:基础理论部分 基础部分面试官主要是问一些简单得原理,口头描述的东西。不会涉及到报文的参数属性等。 OSPF是什么 定义也就是链路状态协议和距离矢量协议的区别区别。 开放式最短路径优先协议 路由是以自己为根,根据数据库计算去往所有树枝节点的最佳路径放进自己…

WebSocket 快速入门 - springboo聊天功能

目录 一、概述 1、HTTP&#xff08;超文本传输协议&#xff09; 2、轮询和长轮询 3、WebSocket 二、WebSocket快速使用 1、基于Java注解实现WebSocket服务器端 2、JS前端测试 三、WebSocket进阶使用 1、如何获取当前用户信息 2、 后端聊天功能实现 一、概述 HTTP…

PVE grub resue错误修复 lvmid BUG

服务器断电后启动不起来&#xff0c;显示grub resue 找了半天没有找到修复方法。看官方文档有一处Recovering from grub “disk not found” error when booting from LVM 极为类似。https://pve.proxmox.com/wiki/Recover_From_Grub_Failure 下面是处理过程。 使用PVE 6.4启…

Leetcode算法训练日记 | day33

专题九 贪心算法 一、跳跃游戏 1.题目 Leetcode&#xff1a;第 55 题 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 …

机器学习(二)之监督学习

前言&#xff1a; 上一节大概讲解了几种学习方式&#xff0c;下面几张就具体来讲讲监督学习的几种算法。 以下示例中和都是权重的意思&#xff01;&#xff01;&#xff01; 注&#xff1a;本文如有错误之处&#xff0c;还请读者指出&#xff0c;欢迎评论区探讨&#xff01; 1…