构造函数,创建
redis
连接,有定时检测功能(一分钟一次心跳检测)
RedisConPool(size_t poolSize, const char* host, int port, const char* pwd)
: poolSize_(poolSize), host_(host), port_(port), b_stop_(false), pwd_(pwd), counter_(0) {
// 循环创建指定数量的 Redis 连接,并将成功连接的 context 对象存入连接池中
for (size_t i = 0; i < poolSize_; ++i) {
// 尝试连接到指定的 Redis 服务器
auto* context = redisConnect(host, port);
// 检查是否连接成功。如果连接失败或连接存在错误,释放资源并跳过本次循环
if (context == nullptr || context->err != 0) {
if (context != nullptr) {
redisFree(context); // 释放连接对象的内存
}
continue;
}
// 发送 AUTH 命令进行密码认证
auto reply = (redisReply*)redisCommand(context, "AUTH %s", pwd);
// 检查认证是否成功。如果失败,打印错误信息,释放 `redisReply` 对象并跳过本次循环
if (reply->type == REDIS_REPLY_ERROR) {
std::cout << "认证失败" << std::endl;
freeReplyObject(reply); // 释放 `redisReply` 对象所占用的内存
continue;
}
// 认证成功,释放 `redisReply` 对象所占用的内存
freeReplyObject(reply);
std::cout << "认证成功" << std::endl;
// 将成功连接的 context 对象添加到连接池中
connections_.push(context);
}
// 启动一个检查线程,用于定期检查连接池中的连接状态
check_thread_ = std::thread([this]() {
while (!b_stop_) {
counter_++; // 计数器递增
// 每隔 60 次循环(即 60 秒)执行一次连接状态检查
if (counter_ >= 60) {
checkThread(); // 调用检查连接状态的方法
counter_ = 0; // 计数器归零
}
// 每次循环后,线程休眠1秒(即每秒进行一次循环)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
- 检测函数自带重连,从队列中取出连接,然后检测。
- 执行PING命令,无论有没有问题都需要还回去,因为PING命令出错只能说明连接无效。
- 若期间出现异常,尝试重新连接
redis
,若出错直接销毁context
,成功则进行认证,认证成功后加入连接池。
void checkThread() {
// 加锁以确保在多线程环境下对 connections_ 的操作是线程安全的
std::lock_guard<std::mutex> lock(mutex_);
// 如果停止标志为 true,则退出函数
if (b_stop_) {
return;
}
// 获取当前连接池的大小
auto pool_size = connections_.size();
// 遍历连接池中的所有连接,检查它们的状态
for (int i = 0; i < pool_size && !b_stop_; i++) {
// 从连接池中取出一个 Redis 连接(但不删除)
auto* context = connections_.front();
connections_.pop(); // 从队列中弹出连接对象
try {
// 向 Redis 服务器发送 PING 命令,检查连接是否有效
auto reply = (redisReply*)redisCommand(context, "PING");
// 如果没有返回有效的回复对象,表示连接可能失效
if (!reply) {
std::cout << "redis ping failed" << std::endl;
// 将此连接重新放回连接池,以便下次重试
connections_.push(context);
continue;
}
// 释放 PING 命令的返回对象,防止内存泄漏
freeReplyObject(reply);
// 将此连接重新放回连接池,因为它是有效的
connections_.push(context);
}
// 捕获在发送 PING 命令或处理过程中抛出的异常
catch (std::exception& exp) {
std::cout << "Error keeping connection alive: " << exp.what() << std::endl;
// 如果捕获到异常,可能说明连接已经失效,释放该连接的资源
redisFree(context);
// 尝试重新连接到 Redis 服务器
context = redisConnect(host_, port_);
if (context == nullptr || context->err != 0) {
if (context != nullptr) {
redisFree(context); // 如果连接失败,释放资源
}
continue; // 连接失败,跳过本次循环
}
// 重新进行认证
auto reply = (redisReply*)redisCommand(context, "AUTH %s", pwd_);
if (reply->type == REDIS_REPLY_ERROR) {
std::cout << "认证失败" << std::endl;
freeReplyObject(reply); // 释放认证失败的返回对象
continue;
}
// 认证成功后,释放返回对象并将新连接加入连接池
freeReplyObject(reply);
std::cout << "认证成功" << std::endl;
connections_.push(context);
}
}
}
其余的连接池实现函数,连接池实现方式都一致:取连接,还连接
void ClearConnections() {
std::lock_guard<std::mutex> lock(mutex_);
while (!connections_.empty()) {
auto* context = connections_.front();
redisFree(context);
connections_.pop();
}
}
redisContext* getConnection() {
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this] {
if (b_stop_) {
return true;
}
return !connections_.empty();
});
//如果停止则直接返回空指针
if (b_stop_) {
return nullptr;
}
auto* context = connections_.front();
connections_.pop();
return context;
}
void returnConnection(redisContext* context) {
std::lock_guard<std::mutex> lock(mutex_);
if (b_stop_) {
return;
}
connections_.push(context);
cond_.notify_one();
}
void Close() {
b_stop_ = true;
cond_.notify_all();
check_thread_.join();
}
Redis管理类设置为单例,在构造函数中初始化Redis连接池(通过读取配置文件来读取必要的数据)
RedisMgr::RedisMgr() {
auto& gCfgMgr = ConfigMgr::Inst();
auto host = gCfgMgr["Redis"]["Host"];
auto port = gCfgMgr["Redis"]["Port"];
auto pwd = gCfgMgr["Redis"]["Passwd"];
_con_pool.reset(new RedisConPool(5, host.c_str(), atoi(port.c_str()), pwd.c_str()));
}
仿照
go
中的defer
实现不同生命周期同一时间执行的功能。也就是在Defer
生命周期结束时,执行传入的函数
// Defer类
class Defer {
public:
// 接受一个lambda表达式或者函数指针
Defer(std::function<void()> func) : func_(func) {}
// 析构函数中执行传入的函数
~Defer() {
func_();
}
private:
std::function<void()> func_;
};