C++连接池

这是我在使用redis做托管服务时用到的连接池,主要以封装hiredis的redisObj对象来作为连接对象的,为了保证连接存活,会定时检测空闲的连接对象是否可以正常工作(正在使用的默认正常),对于操作失败的会重新连接。
我在使用时使用一个单例redis连接池的,初始化后对外提供一个获取连接的方法,构造实例对象便可以获取一条连接,利用类的析构方式,不需要手动放回,在析构时调用放回的方法。

#ifndef __REDISSOURCE_H__
#define __REDISSOURCE_H__
#include <string>
#include <list>
#include "TimeWheel.h"//定时器头文件
#include "Condition.h"//信号量头文件

struct RedisTimerArg;
class RedisObj;//为了特殊情况下的防止头文件污染,在头文件里声明对象,在cpp文件中再包含头文件

class RedisSource{
public:
    static void initRedisSource(std::string strIP,int port,std::string strPassword,
        int initConnectNum,int holdConnectNum,int maxConnectNum,int connectTimeOut);

    static RedisSource* getRedisSourceInstance();

private:

    RedisSource(void);
    RedisSource(std::string strIP, int port = 6379, int initConnectNum = 5, int holdConnectNum = 10, int maxConnectNum = 100);
    ~RedisSource(void);

    static RedisSource *instance;//单例对象
    static TempLock* instanceLock;//锁

    bool isConnect;
    volatile bool isInit;
    std::string strIP;
    int port;
    int initConnectNum;
    int holdConnectNum;
    int maxConnectNum;
    int idleTime;
    unsigned int checkConnIsAliveTimeVal;//检测连接时间间隔
    std::list<RedisObj*> connectList;//连接对象链表
    int haveConnNum;

private:
    TempLock tempLock;
    Condition cond;
    TimeWheel timer;
    std::map<RedisObj*, RedisTimerArg*> monitorConnMap;//检测连接的map
//一些设置的函数,部分可以在初始化后使用,改变连接数或者超时时间
public:
    void SetIp(std::string & strIP);
    void SetPort(int port);
    void SetInitConnectNum(int initConnectNum);
    void SetHoldConnectNum(int holdConnectNum);
    void SetMaxConnectNum(int maxConnectNum);
    void SetIdleTime(long millisecond);
    void SetCheckConnAliveTime(int checkTimeVal);
    void InitRedisSource();
    int AvailConNum();

public:
    RedisObj* OpenConnection();//获取连接池中的一条连接
    void CloseConnection(RedisObj* connection);//将一条连接放回连接池

private:
    RedisObj* connectRedis();
    void closeAllConnection();
    RedisObj* getAndRemoveConn();
    void addMonitorConn(RedisObj* conn);
    void addMonitorConn(RedisTimerArg* arg);
    void cancelMonitorConn(RedisObj* conn);
    void removeAbandonConn(RedisTimerArg* rsTimeArg);
private:
    bool validateParam();

public:
    static void CheckAliveAndCloseIdle(void *conn);
};
#endif

.cpp文件

#pragma once
#include "RedisSource.h"
#include "RedisAPI.h"

//初始化静态变量
RedisSource* RedisSource::instance = NULL;
TempLock* RedisSource::instanceLock = new TempLock();

struct RedisTimerArg
{
    RedisSource* rs;//连接池对象
    RedisObj* conn;//连接对象
    int timeNodeId;//时间任务ID
};
//默认无参构造,初始化一些所需数据
RedisSource::RedisSource(void) :
strIP(""),
port(6379),
initConnectNum(5),
holdConnectNum(10),
maxConnectNum(100),
isInit(false),
haveConnNum(0),
idleTime(10 * 60 * 1000),
checkConnIsAliveTimeVal(10 * 60 * 1000)
{

}

bool RedisSource::validateParam() {
    return true;
}
//初始化连接池
void RedisSource::InitRedisSource(){
    if (isInit || !validateParam()) //持否init
    {
        return;
    }
    isInit = true;
    for (int i = 0; i < initConnectNum; i++)
    {
        RedisObj* conn = new RedisObj();//新建连接对象
        if (conn->initRedisConnect(strIP, port))//判断是否连接成功,这个方法返回的是bool值
        {
            this->connectList.push_back(conn);//将连接放入连接列表中
        }
    }
}
//有参构造函数,包含调用初始化方法
RedisSource::RedisSource(std::string strIP, int port, int initConnectNum, int holdConnectNum, int maxConnectNum) :
strIP(strIP)
, port(port)
, initConnectNum(initConnectNum)
, holdConnectNum(holdConnectNum)
, maxConnectNum(maxConnectNum)
, isInit(false)
, haveConnNum(0)
, idleTime(10 * 60 * 1000)
, checkConnIsAliveTimeVal(4 * 60 * 60 * 1000)
{
    InitRedisSource();
}
//这里有些参数没有用到,预留以后要用,redis的连接方式有需要password的时候
void RedisSource::initRedisSource(std::string strIP, int port, std::string strPassword,
    int initConnectNum, int holdConnectNum, int maxConnectNum, int connectTimeOut)
{
    if (instance == NULL) {
        instanceLock->lock();//防止在创建唯一实例的时候有线程访问这个对象
        if (instance == NULL) {
            instance = new RedisSource(strIP, port, initConnectNum, holdConnectNum, maxConnectNum);
        }
        instanceLock->unlock();
    }
}
//这里返回实例对象
RedisSource* RedisSource::getRedisSourceInstance()
{
    return instance ? instance : NULL;
}
//关闭所有连接,如果调用这个方法,那么一定是不需要有任务执行了,所以全锁住,防止误操作
void RedisSource::closeAllConnection()
{
    tempLock.lock();
    for (auto it = connectList.begin(); it != connectList.end(); ++it)
    {
        (*it)->freeRedisReply();//因为保存的是连接对象的指针,所以解引用后仍然使用箭头访问
        (*it)->freeRedisHandler();
        it = connectList.erase(it);//在链表中删除这个对象
    }

    for (auto it = monitorConnMap.begin(); it != monitorConnMap.end(); ++it)
    {
        delete it->second;//删除map中的对象
        it = monitorConnMap.erase(it);
    }
    tempLock.unlock();
}

RedisSource::~RedisSource(void) {
    closeAllConnection();
}
void RedisSource::SetIp(std::string& strIP) {
    this->strIP = strIP;
}
void RedisSource::SetPort(int port) {
    this->port = port;
}
void RedisSource::SetInitConnectNum(int initConnectNum) {
    this->initConnectNum = initConnectNum;
    InitRedisSource();
}
void RedisSource::SetHoldConnectNum(int holdConnectNum) {
    this->holdConnectNum = holdConnectNum;
    InitRedisSource();
}
void RedisSource::SetMaxConnectNum(int maxConnectNum) {
    this->maxConnectNum = maxConnectNum;
    InitRedisSource();
}
void RedisSource::SetIdleTime(long idleTime) {
    this->idleTime = idleTime;
    InitRedisSource();
}
//获取并删除个连接
RedisObj* RedisSource::getAndRemoveConn()
{
    tempLock.lock();
    while (connectList.size() == 0)
    {
        cond.wait(&tempLock);//如果连接池队列中没有对象了,那么就等
    }
    RedisObj* conn = connectList.front();//获取第一条连接
    connectList.pop_front();//弹出第一条连接
    cancelMonitorConn(conn);//取消检查
    tempLock.unlock();
    return conn;
}
//返回可用的连接数量
int RedisSource::AvailConNum()
{
    tempLock.lock();
    int availConNum = static_cast<int>(connectList.size());
    tempLock.unlock();
    return availConNum;
}
//获取一条连接
RedisObj* RedisSource::OpenConnection()
{
    RedisObj* conn = NULL;
    tempLock.lock();
    if (AvailConNum() == 0 && (haveConnNum < maxConnectNum))//如果可用连接数量为0且连接数小于最大连接数,就创建一个连接对象
    {
        conn = new RedisObj();
        conn->initRedisConnect(strIP,port);//调用连接对象的连接方法
        tempLock.unlock();
    }
    else
    {
        tempLock.unlock();
        conn = getAndRemoveConn();//获取队列中的一条连接
    }
    return conn;
}

void RedisSource::cancelMonitorConn(RedisObj* conn)
{
    RedisTimerArg *rsTimerArg = monitorConnMap[conn];//获取连接检查节点
    if (rsTimerArg != NULL)
    {
        timer.cancelTimer(rsTimerArg->timeNodeId);//取消这个连接的定时检查,这个函数见时间轮算法
        monitorConnMap.erase(conn);//删除这个检查连接的节点
        delete rsTimerArg;
    }
}
//增加检查连接的任务
void RedisSource::addMonitorConn(RedisTimerArg *arg)
{
    arg->timeNodeId = timer.setTimer(checkConnIsAliveTimeVal, CheckAliveAndCloseIdle, arg, NULL);
    monitorConnMap[arg->conn] = arg;

}
//对一个连接对象添加定时检查
void RedisSource::addMonitorConn(RedisObj* conn)
{
    RedisTimerArg* arg = new RedisTimerArg;
    arg->rs = this;
    arg->conn = conn;
    addMonitorConn(arg);
}
//放回一条连接
void RedisSource::CloseConnection(RedisObj* conn)
{
    tempLock.lock();
    addMonitorConn(conn);
    connectList.push_back(conn);
    cond.notify_one();
    tempLock.unlock();
}

void RedisSource::removeAbandonConn(RedisTimerArg *rsTimeArg)
{
    rsTimeArg->rs->monitorConnMap.erase(rsTimeArg->conn);//删除map中的连接对象
    rsTimeArg->rs->connectList.remove(rsTimeArg->conn);//删除连接池中该连接
    rsTimeArg->conn->freeRedisHandler();//释放连接句柄
    delete rsTimeArg;//删除这个节点
}
//检查连接是否可用,检查并关闭多余的空闲连接
void RedisSource::CheckAliveAndCloseIdle(void *rsArg)
{
    RedisTimerArg* rsTimeArg = (RedisTimerArg*)rsArg;
    rsTimeArg->rs->tempLock.lock();

    if (rsTimeArg->rs->connectList.size() > rsTimeArg->rs->holdConnectNum)
    {
        rsTimeArg->rs->removeAbandonConn(rsTimeArg);//删除并且释放这个连接对象
        rsTimeArg->rs->tempLock.unlock();
        return;
    }
    else
    {
        std::string cmd = "test";
        int len;
        rsTimeArg->conn->ListLen(cmd, len);//随便一个不会影响数据的方法,根据返回值判断连接是否正常
        if (rsTimeArg->conn->r_queryReply == NULL)
        {
            rsTimeArg->rs->removeAbandonConn(rsTimeArg);
            rsTimeArg->rs->tempLock.unlock();
            return;
        }
        rsTimeArg->conn->freeRedisReply();
    }

    rsTimeArg->rs->addMonitorConn(rsTimeArg);//重新加入定时检测任务
    rsTimeArg->rs->tempLock.unlock();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值