HTTP服务器中Redis缓存详解
1. 历史演化与背景
1.1 缓存技术的发展历程
在早期的Web应用中,每次用户请求都需要直接访问数据库,这导致了严重的性能瓶颈。随着互联网的发展,缓存技术逐步演化:
- 内存缓存阶段(2000年代初):应用程序在内存中存储常用数据
- 分布式缓存阶段(2005年左右):Memcached的出现,支持多服务器缓存
- Redis时代(2009年至今):Redis(Remote Dictionary Server)提供了更丰富的数据结构和功能
1.2 Redis在HTTP服务器中的作用
Redis作为一个高性能的键值存储系统,在HTTP服务器中主要承担以下角色:
- 数据缓存:缓存频繁访问的数据库查询结果
- 会话存储:存储用户会话信息
- 计数器:实现访问统计、限流等功能
- 消息队列:处理异步任务
2. 核心概念与术语解释
2.1 重要术语及全称
- Redis: Remote Dictionary Server(远程字典服务器)
- TTL: Time To Live(生存时间)
- LRU: Least Recently Used(最近最少使用)
- LFU: Least Frequently Used(最不经常使用)
- AOF: Append Only File(仅追加文件)
- RDB: Redis Database(Redis数据库快照)
- RESP: Redis Serialization Protocol(Redis序列化协议)
- Pipeline: 管道技术,批量执行命令
- Connection Pool: 连接池,管理数据库连接
2.2 基础数据结构
Redis支持多种数据结构:
- String: 字符串,最基本的数据类型
- Hash: 哈希表,类似于编程语言中的字典
- List: 列表,有序的字符串集合
- Set: 集合,无序且唯一的字符串集合
- Sorted Set: 有序集合,每个元素都有一个分数
3. Redis缓存架构设计
3.1 整体架构
HTTP请求 -> Web服务器 -> 缓存层(Redis) -> 数据库
|
v
缓存命中 -> 直接返回
3.2 连接池设计原理
连接池是管理数据库连接的重要组件,它的核心思想是:
- 预创建连接:启动时创建一定数量的连接
- 连接复用:多个请求共享连接,避免频繁创建/销毁
- 连接管理:监控连接状态,自动回收无效连接
4. 核心结构体详解
4.1 Redis连接结构体
cpp
struct RedisConnection {
redisContext* context; // Redis连接上下文
time_t last_used; // 最后使用时间
bool is_connected; // 连接状态
std::string host; // Redis服务器地址
int port; // Redis服务器端口
int database; // 数据库编号(0-15)
};
字段说明:
context
: hiredis库提供的连接上下文,包含socket信息和缓冲区last_used
: 用于连接池的空闲连接回收机制is_connected
: 标识连接是否有效,用于连接健康检查host/port
: 服务器地址信息,用于重连database
: Redis支持16个数据库(0-15),默认使用0号数据库
4.2 缓存项结构体
cpp
struct CacheItem {
std::string key; // 缓存键
std::string value; // 缓存值
time_t expire_time; // 过期时间戳
time_t access_time; // 最后访问时间
int access_count; // 访问次数
size_t size; // 数据大小(字节)
};
字段说明:
key/value
: 基本的键值对数据expire_time
: Unix时间戳,用于TTL过期检查access_time
: 用于LRU淘汰策略access_count
: 用于LFU淘汰策略size
: 用于内存使用量统计和限制
4.3 连接池配置结构体
cpp
struct PoolConfig {
int min_connections; // 最小连接数
int max_connections; // 最大连接数
int connection_timeout; // 连接超时时间(秒)
int idle_timeout; // 空闲超时时间(秒)
int retry_times; // 重试次数
std::string host; // Redis服务器地址
int port; // Redis服务器端口
std::string password; // 认证密码
int database; // 默认数据库
};
5. 核心函数详解
5.1 连接管理函数
createConnection()
功能 : 创建新的Redis连接
入参:
host
: 服务器地址(string类型)port
: 端口号(int类型)timeout
: 超时时间(int类型,单位秒)
返回值 :RedisConnection*
指针,成功返回连接对象,失败返回nullptr
内部流程:
- 调用hiredis的
redisConnectWithTimeout()
建立连接 - 检查连接状态,设置超时参数
- 如果有密码,执行AUTH命令认证
- 选择指定的数据库(SELECT命令)
getConnection()
功能 : 从连接池获取可用连接
入参 : 无
返回值 : RedisConnection*
指针
内部流程:
- 检查空闲连接队列
- 如果有空闲连接,检查连接有效性
- 如果无空闲连接且未达到最大连接数,创建新连接
- 如果达到最大连接数,等待或返回错误
releaseConnection()
功能 : 释放连接回连接池
入参 : conn
- 要释放的连接指针
返回值 : bool
- 成功返回true,失败返回false
内部流程:
- 检查连接有效性
- 更新最后使用时间
- 将连接放回空闲队列
- 通知等待的线程
5.2 缓存操作函数
set()
功能 : 设置缓存键值对
入参:
key
: 缓存键(const std::string&)value
: 缓存值(const std::string&)expire_seconds
: 过期时间(int,单位秒,默认-1表示永不过期)
返回值 :bool
- 成功返回true,失败返回false
内部流程:
- 获取连接池中的连接
- 构造Redis SET命令
- 如果指定了过期时间,使用SETEX命令
- 执行命令并检查返回结果
- 释放连接回池中
get()
功能 : 获取缓存值
入参 : key
- 缓存键(const std::string&)
返回值 : std::string
- 缓存值,不存在返回空字符串
内部流程:
- 获取连接
- 执行GET命令
- 检查返回结果类型(REDIS_REPLY_STRING或REDIS_REPLY_NIL)
- 更新访问统计信息
- 释放连接
exists()
功能 : 检查键是否存在
入参 : key
- 缓存键(const std::string&)
返回值 : bool
- 存在返回true,不存在返回false
内部流程:
- 获取连接
- 执行EXISTS命令
- 检查返回的整数值(1表示存在,0表示不存在)
- 释放连接
del()
功能 : 删除缓存键
入参 : key
- 缓存键(const std::string&)
返回值 : bool
- 成功删除返回true,键不存在返回false
内部流程:
- 获取连接
- 执行DEL命令
- 检查返回的整数值(删除的键数量)
- 释放连接
5.3 过期时间管理函数
expire()
功能 : 设置键的过期时间
入参:
key
: 缓存键(const std::string&)seconds
: 过期秒数(int)
返回值 :bool
- 成功返回true,键不存在返回false
内部流程:
- 获取连接
- 执行EXPIRE命令
- 检查返回结果
- 释放连接
ttl()
功能 : 获取键的剩余生存时间
入参 : key
- 缓存键(const std::string&)
返回值 : int
- 剩余秒数,-1表示永不过期,-2表示键不存在
内部流程:
- 获取连接
- 执行TTL命令
- 返回整数结果
- 释放连接
6. 缓存策略设计
6.1 淘汰策略
Redis支持多种内存淘汰策略:
- noeviction: 不淘汰,内存满时返回错误
- allkeys-lru: 在所有键中使用LRU算法淘汰
- volatile-lru: 在设置了过期时间的键中使用LRU淘汰
- allkeys-lfu: 在所有键中使用LFU算法淘汰
- volatile-lfu: 在设置了过期时间的键中使用LFU淘汰
- allkeys-random: 在所有键中随机淘汰
- volatile-random: 在设置了过期时间的键中随机淘汰
- volatile-ttl: 淘汰即将过期的键
6.2 缓存更新策略
-
Cache-Aside(旁路缓存):
- 读取:先查缓存,缓存未命中再查数据库,然后更新缓存
- 写入:先更新数据库,然后删除缓存
-
Write-Through(写穿透):
- 写入时同时更新缓存和数据库
-
Write-Behind(写回):
- 写入时只更新缓存,异步更新数据库
7. 循序渐进的实现流程
第一步:基础连接实现
- 引入hiredis库
- 实现基本的连接创建和销毁
- 实现简单的SET/GET操作
第二步:连接池实现
- 设计连接池结构
- 实现连接的获取和释放
- 添加连接健康检查
第三步:缓存策略实现
- 实现TTL过期机制
- 添加访问统计
- 实现基本的淘汰策略
第四步:高级功能
- 实现批量操作
- 添加事务支持
- 实现发布订阅功能
第五步:性能优化
- 实现管道技术
- 添加连接复用
- 实现异步操作
8. 错误处理与监控
8.1 常见错误类型
- 连接错误: 网络问题、服务器宕机
- 认证错误: 密码错误、权限不足
- 内存错误: Redis内存不足
- 命令错误: 语法错误、类型错误
8.2 监控指标
- 连接池状态: 活跃连接数、空闲连接数
- 缓存命中率: 命中次数/总请求次数
- 响应时间: 平均响应时间、99分位响应时间
- 内存使用: Redis内存使用量、键数量
HTTP 服务器 Redis 缓存组件
1. Redis 客户端连接池 (RedisClientPool)
功能:
管理一系列到 Redis 服务器的长连接(TCP连接)。其核心目标是避免为每个HTTP请求都重新建立和断开连接的开销,从而极大提升性能。
为什么需要:
- 减少延迟: 建立 TCP 连接需要三次握手,开销很大。
- 降低服务器负载: 频繁创建连接会消耗 Redis 服务器和应用的 CPU 和内存资源。
- 控制连接数: 防止客户端在高并发时创建过多连接,耗尽 Redis 服务器的最大连接数资源。
主要成员变量:
cpp
class RedisClientPool {
private:
std::queue<redisContext*> idle_connections_; // 空闲连接队列
std::vector<redisContext*> all_connections_; // 所有连接(可选,用于最终释放)
int min_connections_; // 最小保持的空闲连接数
int max_connections_; // 池中允许的最大连接数
std::string host_;
int port_;
std::string password_;
std::mutex pool_mutex_; // 互斥锁,保证多线程安全访问连接池
std::condition_variable cv_; // 条件变量,用于连接等待和通知
// ... 其他状态变量,如当前连接数、当前空闲数等 ...
};
2. 缓存管理器 (CacheManager)
功能:
这是缓存系统的大脑和控制器 。它对外提供简洁的 Get
、Set
、Delete
等接口,并内部实现缓存策略(如缓存穿透、缓存击穿、缓存雪崩的应对策略)。
为什么需要:
它封装了所有复杂的缓存逻辑,使业务代码(如 Controller)保持简洁,只需关心"取数据"和"存数据",而不必关心底层实现细节和异常处理。
主要成员变量:
cpp
class CacheManager {
private:
RedisClientPool& redis_pool_; // 持有连接池的引用
// 策略相关的配置项
int default_ttl_; // 默认缓存过期时间(秒)
bool enable_penetration_protection_; // 是否启用缓存穿透保护
// 用于实现"缓存击穿"保护的互斥锁(或分布式锁客户端)
// 例如: std::unordered_map<std::string, std::mutex> key_mutexes_;
// 或者一个分布式锁组件的指针:DistributedLockClient* lock_client_;
// 统计信息(可选)
std::atomic<long long> cache_hits_;
std::atomic<long long> cache_misses_;
};
3. 缓存读写器 (CacheReader / CacheWriter)
功能:
这是 CacheManager
的辅助组件,负责执行具体的 Redis 命令。CacheManager
处理策略,而 CacheReader/Writer
负责与 RedisClientPool
交互,执行具体的 GET
、SET
等命令,并处理基本的错误。
为什么需要:
遵循单一职责原则,将命令执行与策略逻辑分离,使代码更清晰、更易维护。
主要成员变量:
cpp
class CacheReader {
private:
RedisClientPool& pool_; // 从连接池获取连接来执行命令
};
class CacheWriter {
private:
RedisClientPool& pool_;
};
4. 序列化/反序列化组件 (Serializer/Deserializer)
功能:
负责将内存中的数据结构 (如 C++ 对象、JSON、字符串)与存储在 Redis 中的字符串进行相互转换。
为什么需要:
Redis 只能存储字符串、二进制数据等基本类型。而我们的业务数据可能是复杂的对象(如 User
、Product
)。这个组件负责桥接两者。
主要成员变量:
通常这是一个工具类,成员变量很少,主要包含配置项。
cpp
class JsonSerializer { // 例如一个JSON序列化器
public:
// 通常是静态方法或无需成员变量
static std::string Serialize(const User& user);
static User Deserialize(const std::string& json_str);
};
// 或者使用模板类
template<typename T>
class Serializer {
static std::string serialize(const T& obj);
static T deserialize(const std::string& data);
};
5. 后台管理模块 (BackgroundManager) - (可选但重要)
功能:
执行一些异步任务,不与具体的 HTTP 请求绑定。
- 缓存预热: 在服务启动时,加载一些热点数据到缓存中。
- 定时任务: 定期刷新某些缓存,避免其过期。
- 状态统计: 定期收集和报告缓存命中率等指标。
主要成员变量:
cpp
class CacheWarmUpService {
private:
CacheManager& cache_manager_;
DatabaseClient& db_client_; // 用于从源数据库加载数据
std::vector<std::string> hot_keys_; // 需要预热的热点Key列表
};
总结与工作流程
当一个 HTTP 请求到来时:
- Controller 接收到请求,需要获取数据。
- Controller 调用
CacheManager::Get(key)
。 CacheManager
从RedisClientPool
中借出一个连接。CacheManager
使用CacheReader
和Serializer
尝试从 Redis 获取数据。- 如果命中: 反序列化数据后直接返回给 Controller。
- 如果未命中:
CacheManager
执行策略(如使用互斥锁防止缓存击穿)。- 从原始数据源(如 MySQL)查询数据。
- 调用
CacheManager::Set(key, value)
。 CacheManager
使用CacheWriter
和Serializer
将数据写入 Redis。
- 数据返回给 Controller,最终生成 HTTP 响应。
- 连接被归还给
RedisClientPool
。
完整的代码实现
redis_pool.h示例:
cpp
#pragma once
#include <string>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <thread>
#include <atomic>
#include <chrono>
#include <hiredis/hiredis.h>
// Redis连接结构体
// 封装了单个Redis连接的所有信息
struct RedisConnection {
redisContext* context; // hiredis库提供的连接上下文,包含socket和缓冲区信息
time_t last_used; // 最后使用时间戳,用于空闲连接回收
bool is_connected; // 连接状态标识,true表示连接有效
std::string host; // Redis服务器IP地址或域名
int port; // Redis服务器端口号,默认6379
int database; // 当前选择的数据库编号(0-15)
// 构造函数
RedisConnection() : context(nullptr), last_used(0), is_connected(false),
host(""), port(0), database(0) {}
// 析构函数,确保连接被正确释放
~RedisConnection() {
if (context) {
redisFree(context);
context = nullptr;
}
}
};
// 缓存项结构体
// 用于本地缓存统计和管理(可选功能)
struct CacheItem {
std::string key; // 缓存键,唯一标识符
std::string value; // 缓存值,实际存储的数据
time_t expire_time; // 过期时间戳,Unix时间戳格式
time_t access_time; // 最后访问时间,用于LRU淘汰策略
int access_count; // 访问次数计数器,用于LFU淘汰策略
size_t size; // 数据大小(字节),用于内存使用统计
// 构造函数
CacheItem() : key(""), value(""), expire_time(0), access_time(0),
access_count(0), size(0) {}
};
// 连接池配置结构体
// 包含连接池的所有配置参数
struct PoolConfig {
int min_connections; // 最小连接数,连接池启动时创建的连接数量
int max_connections; // 最大连接数,连接池允许的最大连接数量
int connection_timeout; // 连接超时时间(秒),建立连接的最大等待时间
int idle_timeout; // 空闲超时时间(秒),连接空闲多久后被回收
int retry_times; // 重试次数,连接失败时的重试次数
std::string host; // Redis服务器地址
int port; // Redis服务器端口
std::string password; // 认证密码,如果Redis设置了密码
int database; // 默认数据库编号
// 默认构造函数,设置合理的默认值
PoolConfig() : min_connections(5), max_connections(20), connection_timeout(5),
idle_timeout(300), retry_times(3), host("127.0.0.1"),
port(6379), password(""), database(0) {}
};
// Redis连接池类
// 管理Redis连接的创建、分配、回收和销毁
class RedisPool {
private:
PoolConfig config_; // 连接池配置
std::queue<std::shared_ptr<RedisConnection>> idle_connections_; // 空闲连接队列
std::vector<std::shared_ptr<RedisConnection>> all_connections_; // 所有连接的容器
std::mutex pool_mutex_; // 连接池互斥锁,保护并发访问
std::condition_variable pool_condition_; // 条件变量,用于等待可用连接
std::atomic<int> active_connections_; // 当前活跃连接数的原子计数器
std::atomic<bool> is_running_; // 连接池运行状态标识
std::thread cleanup_thread_; // 清理线程,定期回收空闲连接
// 统计信息(用于监控和调试)
std::atomic<long long> total_requests_; // 总请求数
std::atomic<long long> successful_requests_; // 成功请求数
std::atomic<long long> failed_requests_; // 失败请求数
public:
// 构造函数,初始化连接池
explicit RedisPool(const PoolConfig& config);
// 析构函数,清理所有资源
~RedisPool();
// 初始化连接池
// 返回值:bool - 成功返回true,失败返回false
bool initialize();
// 关闭连接池
void shutdown();
// 从连接池获取一个可用连接
// 返回值:shared_ptr<RedisConnection> - 连接对象智能指针,失败返回nullptr
std::shared_ptr<RedisConnection> getConnection();
// 将连接释放回连接池
// 参数:conn - 要释放的连接
void releaseConnection(std::shared_ptr<RedisConnection> conn);
// 基础Redis操作函数
// 设置键值对
// 参数:key - 缓存键,value - 缓存值,expire_seconds - 过期时间(秒,-1表示永不过期)
// 返回值:bool - 成功返回true,失败返回false
bool set(const std::string& key, const std::string& value, int expire_seconds = -1);
// 获取键对应的值
// 参数:key - 缓存键
// 返回值:string - 缓存值,键不存在返回空字符串
std::string get(const std::string& key);
// 检查键是否存在
// 参数:key - 缓存键
// 返回值:bool - 存在返回true,不存在返回false
bool exists(const std::string& key);
// 删除键
// 参数:key - 缓存键
// 返回值:bool - 成功删除返回true,键不存在返回false
bool del(const std::string& key);
// 设置键的过期时间
// 参数:key - 缓存键,seconds - 过期秒数
// 返回值:bool - 成功返回true,键不存在返回false
bool expire(const std::string& key, int seconds);
// 获取键的剩余生存时间
// 参数:key - 缓存键
// 返回值:int - 剩余秒数,-1表示永不过期,-2表示键不存在
int ttl(const std::string& key);
// 批量设置键值对
// 参数:key_values - 键值对的vector,每个pair包含key和value
// 返回值:bool - 全部成功返回true,有失败返回false
bool mset(const std::vector<std::pair<std::string, std::string>>& key_values);
// 批量获取键对应的值
// 参数:keys - 键的vector
// 返回值:vector<string> - 对应的值,不存在的键返回空字符串
std::vector<std::string> mget(const std::vector<std::string>& keys);
// 原子递增操作
// 参数:key - 缓存键,increment - 递增值(默认为1)
// 返回值:long long - 递增后的值,键不存在时从0开始递增
long long incr(const std::string& key, long long increment = 1);
// 原子递减操作
// 参数:key - 缓存键,decrement - 递减值(默认为1)
// 返回值:long long - 递减后的值,键不存在时从0开始递减
long long decr(const std::string& key, long long decrement = 1);
// 获取连接池统计信息
struct PoolStats {
int active_connections; // 当前活跃连接数
int idle_connections; // 当前空闲连接数
int total_connections; // 总连接数
long long total_requests; // 总请求数
long long successful_requests; // 成功请求数
long long failed_requests; // 失败请求数
double success_rate; // 成功率
};
// 获取连接池统计信息
// 返回值:PoolStats - 包含各种统计信息的结构体
PoolStats getStats() const;
// 检查连接池健康状态
// 返回值:bool - 健康返回true,不健康返回false
bool isHealthy() const;
private:
// 创建新的Redis连接
// 返回值:shared_ptr<RedisConnection> - 连接对象,失败返回nullptr
std::shared_ptr<RedisConnection> createConnection();
// 检查连接是否有效
// 参数:conn - 要检查的连接
// 返回值:bool - 有效返回true,无效返回false
bool isConnectionValid(std::shared_ptr<RedisConnection> conn);
// 清理空闲连接的后台线程函数
void cleanupIdleConnections();
// 执行Redis命令的通用函数
// 参数:conn - Redis连接,command - 命令字符串
// 返回值:redisReply* - Redis回复对象,需要调用者释放
redisReply* executeCommand(std::shared_ptr<RedisConnection> conn, const std::string& command);
// 执行Redis命令的通用函数(带参数)
// 参数:conn - Redis连接,format - 命令格式字符串,... - 可变参数
// 返回值:redisReply* - Redis回复对象,需要调用者释放
redisReply* executeCommand(std::shared_ptr<RedisConnection> conn, const char* format, ...);
};
redis_pool.cpp示例
cpp
#include "redis_pool.h"
#include <iostream>
#include <cstring>
#include <cstdarg>
#include <algorithm>
// RedisPool构造函数
// 初始化连接池的基本参数和状态
RedisPool::RedisPool(const PoolConfig& config)
: config_(config), active_connections_(0), is_running_(false),
total_requests_(0), successful_requests_(0), failed_requests_(0) {
// 验证配置参数的合理性
if (config_.min_connections <= 0) {
config_.min_connections = 1;
}
if (config_.max_connections < config_.min_connections) {
config_.max_connections = config_.min_connections * 2;
}
if (config_.connection_timeout <= 0) {
config_.connection_timeout = 5;
}
if (config_.idle_timeout <= 0) {
config_.idle_timeout = 300;
}
}
// RedisPool析构函数
// 确保所有资源被正确释放
RedisPool::~RedisPool() {
shutdown();
}
// 初始化连接池
// 创建最小数量的连接并启动清理线程
bool RedisPool::initialize() {
std::lock_guard<std::mutex> lock(pool_mutex_);
if (is_running_) {
return true; // 已经初始化过了
}
// 创建最小数量的连接
for (int i = 0; i < config_.min_connections; ++i) {
auto conn = createConnection();
if (!conn) {
std::cerr << "Failed to create initial connection " << i << std::endl;
// 清理已创建的连接
while (!idle_connections_.empty()) {
idle_connections_.pop();
}
all_connections_.clear();
return false;
}
idle_connections_.push(conn);
all_connections_.push_back(conn);
}
is_running_ = true;
// 启动清理线程
cleanup_thread_ = std::thread(&RedisPool::cleanupIdleConnections, this);
std::cout << "Redis connection pool initialized with "
<< config_.min_connections << " connections" << std::endl;
return true;
}
// 关闭连接池
// 停止所有线程并释放所有连接
void RedisPool::shutdown() {
{
std::lock_guard<std::mutex> lock(pool_mutex_);
if (!is_running_) {
return; // 已经关闭了
}
is_running_ = false;
}
// 通知所有等待的线程
pool_condition_.notify_all();
// 等待清理线程结束
if (cleanup_thread_.joinable()) {
cleanup_thread_.join();
}
// 清理所有连接
std::lock_guard<std::mutex> lock(pool_mutex_);
while (!idle_connections_.empty()) {
idle_connections_.pop();
}
all_connections_.clear();
std::cout << "Redis connection pool shutdown completed" << std::endl;
}
// 创建新的Redis连接
// 这是连接池的核心功能之一
std::shared_ptr<RedisConnection> RedisPool::createConnection() {
auto conn = std::make_shared<RedisConnection>();
// 设置连接超时
struct timeval timeout = { config_.connection_timeout, 0 };
// 尝试连接Redis服务器
// 注意:这里使用模拟的连接,实际应该使用 redisConnectWithTimeout
// conn->context = redisConnectWithTimeout(config_.host.c_str(), config_.port, timeout);
// 模拟连接创建(实际项目中需要hiredis库)
conn->context = nullptr; // 在实际项目中这里应该是真实的Redis连接
conn->host = config_.host;
conn->port = config_.port;
conn->database = config_.database;
conn->last_used = time(nullptr);
conn->is_connected = true; // 模拟连接成功
// 在实际项目中,这里需要检查连接状态
/*
if (!conn->context || conn->context->err) {
if (conn->context) {
std::cerr << "Redis connection error: " << conn->context->errstr << std::endl;
redisFree(conn->context);
}
return nullptr;
}
*/
// 如果设置了密码,进行认证
if (!config_.password.empty()) {
// 实际项目中执行: AUTH password
// redisReply* reply = (redisReply*)redisCommand(conn->context, "AUTH %s", config_.password.c_str());
// 检查认证结果并释放reply
}
// 选择数据库
if (config_.database != 0) {
// 实际项目中执行: SELECT database
// redisReply* reply = (redisReply*)redisCommand(conn->context, "SELECT %d", config_.database);
// 检查选择结果并释放reply
}
std::cout << "Created new Redis connection to " << config_.host
<< ":" << config_.port << std::endl;
return conn;
}
// 从连接池获取连接
// 这是连接池最重要的功能
std::shared_ptr<RedisConnection> RedisPool::getConnection() {
std::unique_lock<std::mutex> lock(pool_mutex_);
total_requests_++;
// 等待可用连接,最多等待connection_timeout秒
auto timeout = std::chrono::seconds(config_.connection_timeout);
if (!pool_condition_.wait_for(lock, timeout, [this] {
return !is_running_ || !idle_connections_.empty() ||
static_cast<int>(all_connections_.size()) < config_.max_connections;
})) {
failed_requests_++;
std::cerr << "Timeout waiting for available connection" << std::endl;
return nullptr;
}
if (!is_running_) {
failed_requests_++;
return nullptr;
}
std::shared_ptr<RedisConnection> conn;
// 首先尝试从空闲连接中获取
if (!idle_connections_.empty()) {
conn = idle_connections_.front();
idle_connections_.pop();
// 检查连接是否仍然有效
if (!isConnectionValid(conn)) {
// 连接无效,创建新连接
conn = createConnection();
if (!conn) {
failed_requests_++;
return nullptr;
}
// 更新连接列表
auto it = std::find(all_connections_.begin(), all_connections_.end(), conn);
if (it != all_connections_.end()) {
*it = conn;
} else {
all_connections_.push_back(conn);
}
}
}
// 如果没有空闲连接且未达到最大连接数,创建新连接
else if (static_cast<int>(all_connections_.size()) < config_.max_connections) {
conn = createConnection();
if (!conn) {
failed_requests_++;
return nullptr;
}
all_connections_.push_back(conn);
} else {
// 这种情况不应该发生,因为wait_for应该会等待
failed_requests_++;
std::cerr << "No available connections and max limit reached" << std::endl;
return nullptr;
}
// 更新连接使用时间
conn->last_used = time(nullptr);
active_connections_++;
successful_requests_++;
return conn;
}
// 释放连接回连接池
// 连接使用完毕后必须调用此函数
void RedisPool::releaseConnection(std::shared_ptr<RedisConnection> conn) {
if (!conn) {
return;
}
std::lock_guard<std::mutex> lock(pool_mutex_);
if (!is_running_) {
return;
}
// 检查连接是否仍然有效
if (isConnectionValid(conn)) {
conn->last_used = time(nullptr);
idle_connections_.push(conn);
} else {
// 连接无效,从all_connections_中移除
auto it = std::find(all_connections_.begin(), all_connections_.end(), conn);
if (it != all_connections_.end()) {
all_connections_.erase(it);
}
}
active_connections_--;
// 通知等待连接的线程
pool_condition_.notify_one();
}
// 检查连接是否有效
// 用于连接健康检查
bool RedisPool::isConnectionValid(std::shared_ptr<RedisConnection> conn) {
if (!conn || !conn->is_connected) {
return false;
}
// 在实际项目中,这里应该发送PING命令来检查连接
/*
if (!conn->context || conn->context->err) {
return false;
}
redisReply* reply = (redisReply*)redisCommand(conn->context, "PING");
if (!reply) {
return false;
}
bool valid = (reply->type == REDIS_REPLY_STATUS &&
strcmp(reply->str, "PONG") == 0);
freeReplyObject(reply);
return valid;
*/
// 模拟连接检查
return true;
}
// 清理空闲连接的后台线程
// 定期检查并回收长时间空闲的连接
void RedisPool::cleanupIdleConnections() {
while (is_running_) {
std::this_thread::sleep_for(std::chrono::seconds(60)); // 每60秒检查一次
std::lock_guard<std::mutex> lock(pool_mutex_);
if (!is_running_) {
break;
}
time_t now = time(nullptr);
std::queue<std::shared_ptr<RedisConnection>> new_idle_queue;
// 检查空闲连接,移除过期的连接
while (!idle_connections_.empty()) {
auto conn = idle_connections_.front();
idle_connections_.pop();
if (now - conn->last_used < config_.idle_timeout &&
isConnectionValid(conn) &&
static_cast<int>(all_connections_.size()) > config_.min_connections) {
new_idle_queue.push(conn);
} else {
// 移除过期或无效的连接
auto it = std::find(all_connections_.begin(), all_connections_.end(), conn);
if (it != all_connections_.end()) {
all_connections_.erase(it);
}
std::cout << "Removed idle connection" << std::endl;
}
}
idle_connections_ = new_idle_queue;
}
}
// 执行Redis命令的通用函数
// 这是所有Redis操作的基础
redisReply* RedisPool::executeCommand(std::shared_ptr<RedisConnection> conn, const char* format, ...) {
if (!conn || !conn->context) {
return nullptr;
}
// 在实际项目中,这里使用vsnprintf和redisCommand
/*
va_list args;
va_start(args, format);
redisReply* reply = (redisReply*)redisvCommand(conn->context, format, args);
va_end(args);
return reply;
*/
// 模拟命令执行
return nullptr;
}
// SET操作:设置键值对
// 这是最基本的缓存操作
bool RedisPool::set(const std::string& key, const std::string& value, int expire_seconds) {
auto conn = getConnection();
if (!conn) {
return false;
}
bool result = false;
// 在实际项目中执行Redis命令
/*
redisReply* reply;
if (expire_seconds > 0) {
// 使用SETEX命令设置带过期时间的键值对
reply = (redisReply*)redisCommand(conn->context, "SETEX %s %d %s",
key.c_str(), expire_seconds, value.c_str());
} else {
// 使用SET命令设置永久键值对
reply = (redisReply*)redisCommand(conn->context, "SET %s %s",
key.c_str(), value.c_str());
}
if (reply && reply->type == REDIS_REPLY_STATUS &&
strcmp(reply->str, "OK") == 0) {
result = true;
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟SET操作成功
result = true;
std::cout << "SET " << key << " = " << value;
if (expire_seconds > 0) {
std::cout << " (expires in " << expire_seconds << " seconds)";
}
std::cout << std::endl;
releaseConnection(conn);
return result;
}
// GET操作:获取键对应的值
// 这是最基本的缓存读取操作
std::string RedisPool::get(const std::string& key) {
auto conn = getConnection();
if (!conn) {
return "";
}
std::string result;
// 在实际项目中执行Redis命令
/*
redisReply* reply = (redisReply*)redisCommand(conn->context, "GET %s", key.c_str());
if (reply) {
if (reply->type == REDIS_REPLY_STRING) {
result = std::string(reply->str, reply->len);
}
// reply->type == REDIS_REPLY_NIL 表示键不存在,返回空字符串
freeReplyObject(reply);
}
*/
// 模拟GET操作
result = "mock_value_for_" + key;
std::cout << "GET " << key << " = " << result << std::endl;
releaseConnection(conn);
return result;
}
// EXISTS操作:检查键是否存在
bool RedisPool::exists(const std::string& key) {
auto conn = getConnection();
if (!conn) {
return false;
}
bool result = false;
// 在实际项目中执行Redis命令
/*
redisReply* reply = (redisReply*)redisCommand(conn->context, "EXISTS %s", key.c_str());
if (reply && reply->type == REDIS_REPLY_INTEGER) {
result = (reply->integer == 1);
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟EXISTS操作
result = true; // 假设键存在
std::cout << "EXISTS " << key << " = " << (result ? "true" : "false") << std::endl;
releaseConnection(conn);
return result;
}
// DEL操作:删除键
bool RedisPool::del(const std::string& key) {
auto conn = getConnection();
if (!conn) {
return false;
}
bool result = false;
// 在实际项目中执行Redis命令
/*
redisReply* reply = (redisReply*)redisCommand(conn->context, "DEL %s", key.c_str());
if (reply && reply->type == REDIS_REPLY_INTEGER) {
result = (reply->integer > 0); // 返回删除的键数量
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟DEL操作
result = true;
std::cout << "DEL " << key << " = " << (result ? "success" : "failed") << std::endl;
releaseConnection(conn);
return result;
}
// EXPIRE操作:设置键的过期时间
bool RedisPool::expire(const std::string& key, int seconds) {
auto conn = getConnection();
if (!conn) {
return false;
}
bool result = false;
// 在实际项目中执行Redis命令
/*
redisReply* reply = (redisReply*)redisCommand(conn->context, "EXPIRE %s %d",
key.c_str(), seconds);
if (reply && reply->type == REDIS_REPLY_INTEGER) {
result = (reply->integer == 1); // 1表示成功,0表示键不存在
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟EXPIRE操作
result = true;
std::cout << "EXPIRE " << key << " " << seconds << " = "
<< (result ? "success" : "failed") << std::endl;
releaseConnection(conn);
return result;
}
// TTL操作:获取键的剩余生存时间
int RedisPool::ttl(const std::string& key) {
auto conn = getConnection();
if (!conn) {
return -2; // 连接失败
}
int result = -2;
// 在实际项目中执行Redis命令
/*
redisReply* reply = (redisReply*)redisCommand(conn->context, "TTL %s", key.c_str());
if (reply && reply->type == REDIS_REPLY_INTEGER) {
result = static_cast<int>(reply->integer);
// -1: 键存在但没有设置过期时间
// -2: 键不存在
// >0: 剩余秒数
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟TTL操作
result = 300; // 假设还有300秒过期
std::cout << "TTL " << key << " = " << result << " seconds" << std::endl;
releaseConnection(conn);
return result;
}
// MSET操作:批量设置键值对
bool RedisPool::mset(const std::vector<std::pair<std::string, std::string>>& key_values) {
if (key_values.empty()) {
return true;
}
auto conn = getConnection();
if (!conn) {
return false;
}
bool result = false;
// 在实际项目中,构造MSET命令
/*
std::string command = "MSET";
for (const auto& kv : key_values) {
command += " " + kv.first + " " + kv.second;
}
redisReply* reply = (redisReply*)redisCommand(conn->context, command.c_str());
if (reply && reply->type == REDIS_REPLY_STATUS &&
strcmp(reply->str, "OK") == 0) {
result = true;
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟MSET操作
result = true;
std::cout << "MSET ";
for (const auto& kv : key_values) {
std::cout << kv.first << "=" << kv.second << " ";
}
std::cout << "= success" << std::endl;
releaseConnection(conn);
return result;
}
// MGET操作:批量获取键对应的值
std::vector<std::string> RedisPool::mget(const std::vector<std::string>& keys) {
std::vector<std::string> results;
if (keys.empty()) {
return results;
}
auto conn = getConnection();
if (!conn) {
return results;
}
// 在实际项目中,构造MGET命令
/*
std::string command = "MGET";
for (const auto& key : keys) {
command += " " + key;
}
redisReply* reply = (redisReply*)redisCommand(conn->context, command.c_str());
if (reply && reply->type == REDIS_REPLY_ARRAY) {
for (size_t i = 0; i < reply->elements; ++i) {
if (reply->element[i]->type == REDIS_REPLY_STRING) {
results.push_back(std::string(reply->element[i]->str,
reply->element[i]->len));
} else {
results.push_back(""); // 键不存在
}
}
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟MGET操作
for (const auto& key : keys) {
results.push_back("mock_value_for_" + key);
}
std::cout << "MGET ";
for (const auto& key : keys) {
std::cout << key << " ";
}
std::cout << "= " << results.size() << " values" << std::endl;
releaseConnection(conn);
return results;
}
// INCR操作:原子递增
long long RedisPool::incr(const std::string& key, long long increment) {
auto conn = getConnection();
if (!conn) {
return 0;
}
long long result = 0;
// 在实际项目中执行Redis命令
/*
redisReply* reply;
if (increment == 1) {
reply = (redisReply*)redisCommand(conn->context, "INCR %s", key.c_str());
} else {
reply = (redisReply*)redisCommand(conn->context, "INCRBY %s %lld",
key.c_str(), increment);
}
if (reply && reply->type == REDIS_REPLY_INTEGER) {
result = reply->integer;
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟INCR操作
result = increment; // 假设从0开始递增
std::cout << "INCR " << key << " by " << increment << " = " << result << std::endl;
releaseConnection(conn);
return result;
}
// DECR操作:原子递减
long long RedisPool::decr(const std::string& key, long long decrement) {
auto conn = getConnection();
if (!conn) {
return 0;
}
long long result = 0;
// 在实际项目中执行Redis命令
/*
redisReply* reply;
if (decrement == 1) {
reply = (redisReply*)redisCommand(conn->context, "DECR %s", key.c_str());
} else {
reply = (redisReply*)redisCommand(conn->context, "DECRBY %s %lld",
key.c_str(), decrement);
}
if (reply && reply->type == REDIS_REPLY_INTEGER) {
result = reply->integer;
}
if (reply) {
freeReplyObject(reply);
}
*/
// 模拟DECR操作
result = -decrement; // 假设从0开始递减
std::cout << "DECR " << key << " by " << decrement << " = " << result << std::endl;
releaseConnection(conn);
return result;
}
// 获取连接池统计信息
RedisPool::PoolStats RedisPool::getStats() const {
std::lock_guard<std::mutex> lock(pool_mutex_);
PoolStats stats;
stats.active_connections = active_connections_.load();
stats.idle_connections = static_cast<int>(idle_connections_.size());
stats.total_connections = static_cast<int>(all_connections_.size());
stats.total_requests = total_requests_.load();
stats.successful_requests = successful_requests_.load();
stats.failed_requests = failed_requests_.load();
if (stats.total_requests > 0) {
stats.success_rate = static_cast<double>(stats.successful_requests) /
static_cast<double>(stats.total_requests) * 100.0;
} else {
stats.success_rate = 0.0;
}
return stats;
}
// 检查连接池健康状态
bool RedisPool::isHealthy() const {
if (!is_running_) {
return false;
}
auto stats = getStats();
// 检查是否有足够的连接
if (stats.total_connections < config_.min_connections) {
return false;
}
// 检查成功率是否足够高
if (stats.total_requests > 100 && stats.success_rate < 95.0) {
return false;
}
return true;
}