当前位置: 首页 > news >正文

知识随记-----使用现代C++客户端库redis-plus-plus实现redis池缓解高并发

🚀 知识随记-----使用现代C++客户端库redis-plus-plus实现redis池缓解高并发
📅 更新时间:2025年7月31日
🏷️ 标签:现代化C++ | redis | redis-plus-plus | redis池 | 高并发

项目场景:

当我们需要用redis的时候,比如实现邮箱验证服务,我们需要判断验证码是否过期,可以使用redis,让邮箱为key,验证码为value,设置过期时间


问题描述

多客户端需验证码场景下,若仅用单个 Redis,每次交互均需执行 Redis 客户端实例化→建立连接→执行操作→断开连接 流程,造成高频连接创建与销毁的资源浪费


原因分析:

高频连接创建与销毁造成资源浪费的核心原因在于:​

1.连接建立的固有开销每次新建连接需经历 TCP 三次握手、Redis 协议协商及可能的认证流程,这些操作涉及内核态与用户态切换、网络数据包交互,会消耗客户端与服务端的 CPU 算力

2.短连接的资源低效性验证码存储属于短耗时操作(毫秒级),连接建立 / 销毁的耗时(通常几十至数百毫秒)远超业务操作本身,导致大量 CPU 时间被用于非核心业务逻辑

3.连接管理的额外损耗Redis 服务端需为每个连接维护文件描述符、缓冲区等资源,高频创建销毁会引发这些资源的频繁分配与回收,加重内存管理负担

4.并发场景的放大效应多客户端并发请求时,短连接模式会瞬间产生大量连接请求,可能触发系统级别的连接数限制或端口耗尽,进一步加剧资源竞争与响应延迟


解决方案:

通过建立 Redis 连接池(Redis Pool)优化连接管理流程,具体如下:​
1.初始化连接池:项目启动时预先创建并初始化一定数量的 Redis 连接,统一纳入连接池管理,避免后续频繁创建新连接。​
2.连接获取与归还机制:​
定义 GetConnection 函数:当客户端需要操作 Redis 时(如存储 / 校验验证码),从连接池获取已建立的空闲连接,直接复用现有连接资源。​
定义 ReturnRedis 函数:操作完成后,将连接归还给连接池而非直接销毁,使其可被其他客户端再次调用,实现连接的循环复用。​
通过上述方式,可彻底规避高频连接创建与销毁的资源浪费,提升多客户端并发场景下的 Redis 操作效率与系统稳定性

实现redis池:

代码

#pragma once
#include"const.h"class RedisConPool
{
public:RedisConPool(std::size_t PoolSize, const std::string& host, const std::string& port, const std::string& password);~RedisConPool();std::unique_ptr<sw::redis::Redis> GetConnection();void ReturnConnection(std::unique_ptr<sw::redis::Redis> redis);void Close();private:std::size_t _poolsize;std::string _host;std::string _port;std::string _password;std::queue<std::unique_ptr<sw::redis::Redis>> _redis_queue;std::condition_variable _cond;std::atomic<bool> _bstop;std::mutex _mutex;
};
#include "RedisConPool.h"RedisConPool::RedisConPool(std::size_t PoolSize, const std::string& host, const std::string& port, const std::string& password):_host(host),_port(port),
_password(password), _bstop(false), _poolsize(PoolSize)
{for (std::size_t i = 0; i < _poolsize; i++){try{std::string path = "tcp://" + host + ":" + port;auto _redis = std::make_unique<sw::redis::Redis>(path);if (!_password.empty()){_redis->auth(_password);}std::cout << "redis尝试连接" << std::endl;_redis->ping();_redis_queue.push(std::move(_redis));std::cout << "redis成功放入队列" << std::endl;}catch (const sw::redis::Error& e){std::cout << "error in RedisConPool catch and  " ;std::cout << "error is" <<e.what() << std::endl;continue;}}
}RedisConPool::~RedisConPool()
{Close();std::lock_guard<std::mutex> lock(_mutex);while (!_redis_queue.empty()){_redis_queue.pop();}
}void RedisConPool::Close()
{_bstop = true;_cond.notify_all();
}std::unique_ptr<sw::redis::Redis> RedisConPool::GetConnection()
{if (_bstop)return nullptr;std::unique_lock<std::mutex> lock(_mutex);_cond.wait(lock, [this]() {if (_bstop)return false;return !_redis_queue.empty();});if (_bstop)return nullptr;auto redis =std::move( _redis_queue.front());_redis_queue.pop();return redis;
}void RedisConPool::ReturnConnection(std::unique_ptr<sw::redis::Redis> redis)
{if (redis == nullptr)return;std::lock_guard<std::mutex> lock(_mutex);if (_bstop)return;try{redis->ping();_redis_queue.push(std::move(redis));_cond.notify_one();}catch(const sw::redis::Error& e){std::cout << "返回无效,丢弃连接: " << e.what() << std::endl;}
}

细节点

std::size_t _poolsize 定义池大小

std::string _host 连接redis所需的地址

std::string _port 连接redis所需的端口号

std::string _password 连接redis所需的密码

std::queue<std::unique_ptr< sw::redis::Redis>> _redis_queue 存储redis的队列

std::condition_variable _cond 与互斥锁(如 std::mutex)配合使用 , 实现线程的等待与唤醒机制

std::atomic _bstop 原子性布尔值 用来判断是否停止

std::mutex _mutex


构造函数

RedisConPool::RedisConPool(std::size_t PoolSize, const std::string& host, 
const std::string& port, const std::string& password)

我们在构造函数中进行初始化,并且将所有redis进行连接并且放入队列


析构函数

RedisConPool::~RedisConPool()

析构函数中我们调用Close()并且要将队列清空


Close()函数
将条件变量变为true 并且发送信号 唤醒所有正在等待的线程


获取redis函数

std::unique_ptr<sw::redis::Redis> RedisConPool::GetConnection()

通过加锁获取池中的redis
如果暂时无法获取,通过std::condition_variable达到线程休眠的机制,提高了并发性 减少了资源浪费

std::condition_variable机制如下:

_cond.wait(参数1,参数2)

wait() 是条件变量的核心函数,用于让当前线程进入等待状态,直到被唤醒且条件满足。它有两个参数:

第一个参数 lock:一个已经加锁的 std::unique_lock< std::mutex> 对象(必须是 unique_lock,因为 wait() 内部需要临时解锁

第二个参数 predicate:一个可调用对象(如函数、lambda 表达式),返回 bool,代表 "线程被唤醒后继续执行的条件

执行流程:
线程被唤醒后,会重新获取互斥锁(此时 lock 再次处于加锁状态),然后检查 predicate:
如果 predicate 返回 true:wait() 函数返回,线程继续执行后续代码(此时条件已满足)。
如果 predicate 返回 false:重复步骤 1,再次释放锁并进入休眠,等待下一次唤醒


归还redis函数

void RedisConPool::ReturnConnection(std::unique_ptr<sw::redis::Redis> redis)

通过加锁,将用完后的redis归还到redis队列中


http://www.lryc.cn/news/606309.html

相关文章:

  • 逻辑回归的应用
  • JVM学习日记(十二)Day12
  • 8K、AI、低空智联,H.266能否撑起下一代视频通路?
  • vue 开发总结:从安装到第一个交互页面-与数据库API
  • 逻辑回归详解:从数学原理到实际应用
  • 三坐标测量仪攻克深孔检测!破解新能源汽车阀体阀孔测量难题
  • MySQL 8.0 OCP 1Z0-908 题目解析(39)
  • Verilog与SytemVerilog差别
  • 文法中的间接左递归
  • 行业热点丨仿真历史数据难以使用?如何利用几何深度学习破局,加速汽车工程创新
  • 【BUUCTF系列】[HCTF 2018]WarmUp1
  • 第15届蓝桥杯C++青少组中级组选拔赛(STEMA)2024年3月10日真题
  • 大模型流式长链接场景下 k8s 优雅退出 JAVA
  • 永磁同步电机无速度算法--直流误差抑制自适应二阶反推观测器
  • 公路坑槽检测分析原理和思路
  • Java——数组及Java某些方法、二维数组
  • #C语言——刷题攻略:牛客编程入门训练(一):简单输出、基本类型
  • C++游戏开发(2)
  • 一次性接收大量上传图片,后端优化方式
  • 代码随想录算法训练营第五十七天|图论part7
  • Qt 消息弹窗 Toast
  • 两款免费数据恢复软件介绍,Win/Mac均可用
  • python后端之DRF框架(下篇)
  • 《零基础入门AI:传统机器学习核心算法(决策树、随机森林与线性回归)》
  • wxPython 实践(五)高级控件
  • 【ad-hoc构造】P10033 「Cfz Round 3」Sum of Permutation|普及+
  • vscode插件开发(腾讯混元)
  • Go再进阶:结构体、接口与面向对象编程
  • Cesium 快速入门(三)Viewer:三维场景的“外壳”
  • 基于深度学习的医学图像分析:使用BERT实现医学文本分类