项目Redis连接池笔记

构造函数,创建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_;
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值