Redis连接池的实现

今天闲着没事干,手搓一下C++ redis连接池。不要问我为什么手搓,因为官方没给!又是羡慕Java的一天。

Java的设计理念:轻轻打下import,剩下的交给jar包

C++的设计理念:道法自然 (给你铁与火,你造不出剑别赖我)

Redis连接池的作用:管理个连接,避免反复创建和销毁带来的开销,还能提高并发量。

一个Redis连接池的基础部分包括定义连接池、连接池初始化、获取连接、回收连接、销毁连接池。同时还要兼顾连接池的线程安全。

本文实现的连接池基于hiredis构建,下载地址:Releases · redis/hiredis · GitHub

连接池头文件定义:

#include <condition_variable>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <vector>

#include "hiredis.h"

using std::list;
using std::map;
using std::string;
using std::vector;

// 这里泛指一个redis连接,这个连接应当包括连接数据库插入删除等操作
class CacheConn{}

class CachePool {
  public:
    // 连接池属性
    CachePool(const char *pool_name, const char *server_ip, int server_port,
              int db_index, const char *password, int max_conn_cnt);
    // 析构函数
    virtual ~CachePool();
    // 初始化方法
    int Init();
    // 获取空闲的连接资源
    CacheConn *GetCacheConn(const int timeout_ms = 0);
    // Pool回收连接资源
    void RelCacheConn(CacheConn *cache_conn);

    const char *GetPoolName() { return pool_name_.c_str(); }
    const char *GetServerIP() { return server_ip_.c_str(); }
    const char *GetPassword() { return password_.c_str(); }
    int GetServerPort() { return m_server_port; }
    int GetDBIndex() { return db_index_; }

  private:
    // 连接池名称
    string pool_name_;
    // 数据库IP地址
    string server_ip_;
    // 数据库密码
    string password_;
    // 端口号
    int m_server_port;
    // redis db index
    int db_index_; 
    // 当前连接的数量
    int cur_conn_cnt_;
    // 最大连接的数量
    int max_conn_cnt_;
    // 空闲连接列表
    list<CacheConn *> free_list_;
    // 互斥锁
    std::mutex m_mutex;
    // 条件变量
    std::condition_variable cond_var_;
    // 中断请求标识
    bool abort_request_ = false;
};

源代码:

连接池的定义、初始化和销毁

CachePool::CachePool(const char *pool_name, const char *server_ip,
                     int server_port, int db_index, const char *password,
                     int max_conn_cnt) {
    pool_name_ = pool_name;
    server_ip_ = server_ip;
    m_server_port = server_port;
    db_index_ = db_index;
    password_ = password;
    max_conn_cnt_ = max_conn_cnt;
    cur_conn_cnt_ = MIN_CACHE_CONN_CNT;
}

CachePool::~CachePool() {
    {
        // 获取互斥锁
        std::lock_guard<std::mutex> lock(m_mutex);
        // 设置中断请求位true
        abort_request_ = true;
        // 通知所有在等待的线程,线程池已经丸啦,该自尽了
        cond_var_.notify_all(); 
    }
    {    
        // 释放所有连接
        std::lock_guard<std::mutex> lock(m_mutex);
        for (list<CacheConn *>::iterator it = free_list_.begin();
             it != free_list_.end(); it++) {
            CacheConn *pConn = *it;
            delete pConn;
        }
    }
    // 清空连接池
    free_list_.clear();
    cur_conn_cnt_ = 0;
}

int CachePool::Init() {
    for (int i = 0; i < cur_conn_cnt_; i++) {
        // 创建一个连接
        CacheConn *pConn =
            new CacheConn(server_ip_.c_str(), m_server_port, db_index_,
                          password_.c_str(), pool_name_.c_str());
        // 初始化失败就删除,返回-1
        if (pConn->Init()) {
            delete pConn;
            return 1;
        }
        创建成功后存储进入free_list_
        free_list_.push_back(pConn);
    }

    log_info("cache pool: %s, list size: %lu\n", pool_name_.c_str(),
             free_list_.size());
    return 0;
}

从连接池中获取一个redis连接

CacheConn *CachePool::GetCacheConn(const int timeout_ms) {
    // 获取独占锁
    std::unique_lock<std::mutex> lock(m_mutex);
    // 收到终止标识直接退出
    if (abort_request_) {
        log_info("have aboort\n");
        return NULL;
    }
    // 2 队列中没有任何可用连接
    if (free_list_.empty()) 
    {
        // 第一步先检测 当前连接数量是否达到最大的连接数量
        if (cur_conn_cnt_ >= max_conn_cnt_) // 等待的逻辑
        {
            // 如果已经到达了,看看是否需要超时等待
            if (timeout_ms <= 0) // 死等,直到有连接可以用 或者 连接池要退出
            {
                log_info("wait ms:%d\n", timeout_ms);
                cond_var_.wait(lock, [this] {
                    // 当前连接数量小于最大连接数量 或者请求释放连接池时退出
                    return (!free_list_.empty()) | abort_request_;
                });
            } else {
                // return如果返回 false,继续wait(或者超时),
                // 如果返回true退出wait 1.m_free_list不为空 2.超时退出
                // 3. m_abort_request被置为true,要释放整个连接池
                cond_var_.wait_for(
                    lock, std::chrono::milliseconds(timeout_ms),
                    [this] { return (!free_list_.empty()) | abort_request_; });
                // 带超时功能时还要判断是否为空
                if (free_list_.empty()) // 如果连接池还是没有空闲则退出
                {
                    return NULL;
                }
            }

            if (abort_request_) {
                log_warn("have aboort\n");
                return NULL;
            }
        } else // 还没有到最大连接则创建连接
        {
            CacheConn *db_conn =
                new CacheConn(server_ip_.c_str(), m_server_port, db_index_,
                              password_.c_str(), pool_name_.c_str()); //新建连接
            int ret = db_conn->Init();
            if (ret) {
                log_error("Init DBConnecton failed\n\n");
                delete db_conn;
                return NULL;
            } else {
                free_list_.push_back(db_conn);
                cur_conn_cnt_++;
                // log_info("new db connection: %s, conn_cnt: %d\n",
                // m_pool_name.c_str(), m_cur_conn_cnt);
            }
        }
    }
    // 队列中有可用连接直接取出第一个连接然后返回
    CacheConn *pConn = free_list_.front();
    free_list_.pop_front();

    return pConn;
}

归还一个连接:

void CachePool::RelCacheConn(CacheConn *p_cache_conn) {
    std::lock_guard<std::mutex> lock(m_mutex);
    // 遍历连接池看看是不是已经存在
    list<CacheConn *>::iterator it = free_list_.begin();
    for (; it != free_list_.end(); it++) {
        if (*it == p_cache_conn) {
            break;
        }
    }
    // 不存在就放进去,存在说明某个地方逻辑写错了,把错误信息写到日志里面
    if (it == free_list_.end()) {
        // m_used_list.remove(pConn);
        free_list_.push_back(p_cache_conn);
        cond_var_.notify_one(); // 通知取队列
    } else {
        log_error("RelDBConn failed\n"); // 不再次回收连接
    }
}

好累啊,晚上去吃华莱士奖励一下自己,这是一条吃饭博客,由挨踢零声赞助。

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值