在个人的项目中使用到了mysql连接池与redis连接池,记录一下编码的思路和调试过程中遇到的一些问题。
1 整体实现思想
mysql与redis均使用到了连接池,因此将其从一个公有抽象类中继承出来,每个连接池实现自己的类方法
template<typename T>
class connection_pool
{
public:
virtual void init() = 0;
virtual T * GetConnection() = 0; //获取数据库连接
virtual bool ReleaseConnection(T *conn) = 0; //释放连接
virtual int GetFreeConn() = 0; //获取连接
virtual void DestroyPool() = 0;
};
class mysql_connection_pool:public connection_pool<mysql_conn>
{
public:
mysql_conn * GetConnection();
bool ReleaseConnection(mysql_conn *conn);
int GetFreeConn();
void DestroyPool();
static mysql_connection_pool *GetInstance();
void init();
static int m_MaxConn;
private:
mysql_connection_pool();
~mysql_connection_pool();
int m_CurConn; //当前已使用的连接数
int m_FreeConn; //当前空闲的连接数
locker lock;
list<mysql_conn *> connList;
sem reserve;
};
class redis_connection_pool:public connection_pool<redis_conn>
{
public:
redis_conn * GetConnection(); //获取数据库连接
bool ReleaseConnection(redis_conn *conn); //释放连接
int GetFreeConn(); //获取连接
void DestroyPool(); //销毁连接池
void init();
public:
static redis_connection_pool* GetInstance();
static int m_MaxConn;
private:
redis_connection_pool();
~redis_connection_pool();
list<redis_conn *> connList;
sem reserve;
locker lock;
int m_CurConn;
int m_FreeConn;
};
以上即派生出了mysql连接池与redis连接池,为了保证只有一个对象对声明出来,即将两个池均设计为了单例模式
mysql_connection_pool *mysql_connection_pool::GetInstance()
{
static mysql_connection_pool connPool;
return &connPool;
}
以上以为一个线程安全的单例模式,之所以没有上锁,C++11标准中,局部静态变量初始化具有线程安全性。
在简单实现了连接池的设计之后,还剩下一个主要的问题如果我们获取到连接之后,如果忘记及时的释放连接,会导致线程池中其他线程获取步到连接,无法处理请求,为了解决这个问题,可以利用RAII机制来帮助我们释放资源,即利用对象的生存周期来对资源进行管理,智能指针正是用这样的思想实现的,下面贴出代码:
template <typename T1, typename T2>
class connectionRAII
{
public:
connectionRAII(T1 * &con, T2 *connPool);
~connectionRAII();
private:
T1 *conRAII;
T2 *poolRAII;
};
template <typename T1, typename T2>
connectionRAII<T1, T2>::connectionRAII(T1 * &con, T2 *connPool)
{
con = connPool->GetConnection();
conRAII = con;
poolRAII = connPool;
}
template <typename T1, typename T2>
connectionRAII<T1,T2>::~connectionRAII()
{
poolRAII->ReleaseConnection(conRAII);
}
至此,两个连接池即实现了。
2 调试过程
1 mysql_real_connection 段错误
在调试过程中,发生段错误利用GDB调试定位到mysql_real_connection这个函数发生段错误,进一步跟踪发现是在初始化MYSQL句柄错误,不可以直接使用指针来进行初始化,应该使用一个局部变量初始化,如下代码:
MYSQL mysql_con;
if(!mysql_init(&mysql_con))
{
LOG_ERROR("MySQL init Error");
exit(1);
}
this->conn = mysql_real_connect(&mysql_con, addr.c_str(), user.c_str(), passwd.c_str(), db.c_str(), 3306, NULL, 0);
if(conn == NULL)
{
LOG_ERROR("MySQL connect Error is %s",mysql_error(mysql_con));
exit(1);
}
PS:该问题一直没有想通是为什么,打算有时间的看一下相关源码,找一下具体原因
2 在创建MYSQL连接句柄时,创建了8个相同的句柄
对此,仍然是第一个问题中初始化的问题,将MYSQL mysql_con 改为动态对象初始化即可解决问题。