这是我在使用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();
}