赞
踩
本文主要介绍基于MySQL数据库、使用C++语言实现数据库连接池的方法。
在本文中,我们利用MySQL提供的“mysql-connector-c++”以及boost编写数据库连接池。
“mysql-connector-c++”可以从MySQL官网上下载,如下图:
说明:
在上述页面中下载下来的文件,直接包含了“mysql-connector”的头文件和共享库,如下:
注意:“mysql-connector”提供的头文件“mysql_connection.h”放在了“jdbc”目录下(如上图)。
使用Yum命令直接安装,如下:
yum install boost-devel.x86_64
数据库连接池头文件(connection_pool.h),代码如下:
- #ifndef __CONNECTION_POOL_H__
- #define __CONNECTION_POOL_H__
-
- #include <mysql_connection.h>
- #include <mysql_driver.h>
- #include <cppconn/exception.h>
- #include <cppconn/driver.h>
- #include <cppconn/connection.h>
- #include <cppconn/resultset.h>
- #include <cppconn/prepared_statement.h>
- #include <cppconn/statement.h>
- #include <pthread.h>
- #include <list>
- #include <string>
-
- using namespace std;
- using namespace sql;
-
- class ConnPool
- {
- public:
- ~ConnPool();
- // 获取数据库连接
- Connection* GetConnection();
- // 将数据库连接放回到连接池的容器中
- void ReleaseConnection(Connection *conn);
- // 获取数据库连接池对象
- static ConnPool* GetInstance();
-
- private:
- // 当前已建立的数据库连接数量
- int curSize;
- // 连接池定义的最大数据库连接数
- int maxSize;
- string username;
- string password;
- string url;
- // 连接池容器
- list<Connection*> connList;
- // 线程锁
- pthread_mutex_t lock;
- static ConnPool* connPool;
- Driver* driver;
-
- // 创建一个连接
- Connection* CreateConnection();
- // 初始化数据库连接池
- void InitConnection(int iInitialSize);
- // 销毁数据库连接对象
- void DestoryConnection(Connection *conn);
- // 销毁数据库连接池
- void DestoryConnPool();
- // 构造方法
- ConnPool(string url, string user,string password, int maxSize);
- };
-
- #endif/*_CONNECTION_POOL_H */
-
数据库连接池实现文件(connection_pool.cpp),代码如下:
- #include <stdexcept>
- #include <exception>
- #include <stdio.h>
- #include "connection_pool.h"
-
- using namespace std;
- using namespace sql;
-
- ConnPool* ConnPool::connPool = NULL;
-
- // 获取连接池对象,单例模式
- ConnPool* ConnPool::GetInstance()
- {
- if (connPool == NULL)
- {
- connPool = new ConnPool("tcp://192.168.213.130:3306", "root", "", 20);
- }
-
- return connPool;
- }
-
- // 数据库连接池的构造函数
- ConnPool::ConnPool(string url, string userName, string password, int maxSize)
- {
- this->maxSize = maxSize;
- this->curSize = 0;
- this->username = userName;
- this->password = password;
- this->url = url;
-
- try
- {
- this->driver = sql::mysql::get_driver_instance();
- }
- catch (sql::SQLException& e)
- {
- perror("get driver error.\n");
- }
- catch (std::runtime_error& e)
- {
- perror("[ConnPool] run time error.\n");
- }
-
- // 在初始化连接池时,建立一定数量的数据库连接
- this->InitConnection(maxSize/2);
- }
-
- // 初始化数据库连接池,创建最大连接数一半的连接数量
- void ConnPool::InitConnection(int iInitialSize)
- {
- Connection* conn;
- pthread_mutex_lock(&lock);
-
- for (int i = 0; i < iInitialSize; i++)
- {
- conn = this->CreateConnection();
- if (conn)
- {
- connList.push_back(conn);
- ++(this->curSize);
- }
- else
- {
- perror("Init connection error.");
- }
- }
-
- pthread_mutex_unlock(&lock);
- }
-
- // 创建并返回一个连接
- Connection* ConnPool::CreateConnection()
- {
- Connection* conn;
- try
- {
- // 建立连接
- conn = driver->connect(this->url, this->username, this->password);
- return conn;
- }
- catch (sql::SQLException& e)
- {
- perror("create connection error.");
- return NULL;
- }
- catch (std::runtime_error& e)
- {
- perror("[CreateConnection] run time error.");
- return NULL;
- }
- }
-
- // 从连接池中获得一个连接
- Connection* ConnPool::GetConnection()
- {
- Connection* con;
- pthread_mutex_lock(&lock);
-
- // 连接池容器中还有连接
- if (connList.size() > 0)
- {
- // 获取第一个连接
- con = connList.front();
- // 移除第一个连接
- connList.pop_front();
- // 判断获取到的连接的可用性
- // 如果连接已经被关闭,删除后重新建立一个
- if (con->isClosed())
- {
- delete con;
- con = this->CreateConnection();
- // 如果连接为空,说明创建连接出错
- if (con == NULL)
- {
- // 从容器中去掉这个空连接
- --curSize;
- }
- }
-
- pthread_mutex_unlock(&lock);
- return con;
- }
- // 连接池容器中没有连接
- else
- {
- // 当前已创建的连接数小于最大连接数,则创建新的连接
- if (curSize < maxSize)
- {
- con = this->CreateConnection();
- if (con)
- {
- ++curSize;
- pthread_mutex_unlock(&lock);
- return con;
- }
- else
- {
- pthread_mutex_unlock(&lock);
- return NULL;
- }
- }
- // 当前建立的连接数已经达到最大连接数
- else
- {
- perror("[GetConnection] connections reach the max number.");
- pthread_mutex_unlock(&lock);
- return NULL;
- }
- }
- }
-
- // 释放数据库连接,将该连接放回到连接池中
- void ConnPool::ReleaseConnection(sql::Connection* conn)
- {
- if (conn)
- {
- pthread_mutex_lock(&lock);
-
- connList.push_back(conn);
-
- pthread_mutex_unlock(&lock);
- }
- }
-
- // 数据库连接池的析构函数
- ConnPool::~ConnPool()
- {
- this->DestoryConnPool();
- }
-
- // 销毁连接池,需要先销毁连接池的中连接
- void ConnPool::DestoryConnPool()
- {
- list<Connection*>::iterator itCon;
- pthread_mutex_lock(&lock);
-
- for (itCon = connList.begin(); itCon != connList.end(); ++itCon)
- {
- // 销毁连接池中的连接
- this->DestoryConnection(*itCon);
- }
- curSize = 0;
- // 清空连接池中的连接
- connList.clear();
-
- pthread_mutex_unlock(&lock);
- }
-
- // 销毁数据库连接
- void ConnPool::DestoryConnection(Connection* conn)
- {
- if (conn)
- {
- try
- {
- // 关闭连接
- conn->close();
- }
- catch(sql::SQLException& e)
- {
- perror(e.what());
- }
- catch(std::exception& e)
- {
- perror(e.what());
- }
- // 删除连接
- delete conn;
- }
- }
数据库连接池测试代码(test_dbpool.cpp):
- #include "connection_pool.h"
-
- //初始化连接池
- ConnPool *connpool = ConnPool::GetInstance();
-
- int main(int argc, char* argv[])
- {
- Connection *con;
- Statement *state;
- ResultSet *result;
-
- // 从连接池中获取连接
- con = connpool->GetConnection();
-
- for (int i = 0; i < 100; i++)
- {
- state = con->createStatement();
- state->execute("use testdb");
-
- // 查询数据库
- result = state->executeQuery("select * from roles");
- // 打印数据库查询结果
- cout << "================================" << endl;
- while (result->next())
- {
- int nRoleId = result->getInt("role_id");
- string strOccupation = result->getString("occupation");
- string strCamp = result->getString("camp");
- cout << nRoleId << " , " << strOccupation << " , " << strCamp << endl;
- }
- cout << "i is: " << i << endl;
- cout << "================================" << endl;
- }
-
- delete result;
- delete state;
- connpool->ReleaseConnection(con);
-
- return 0;
- }
编译上述文件,生成数据库连接池测试程序,如下:
g++ -o test_dbpool test_dbpool.cpp connection_pool.cpp -I /opt/liitdar/mysql-connector-c++-8.0.11-linux-el7-x86-64bit/include/jdbc/ -L /opt/liitdar/mysql-connector-c++-8.0.11-linux-el7-x86-64bit/lib64/ -lmysqlcppconn
执行上面生成的测试程序,命令如下:
./test_dbpool
程序执行(部分)结果如下:
上述结果表明我们编写的数据库连接池正常运行了。
这里对数据库连接池的实现代码进行简单地总结。
数据库连接池头文件(connection_pool.h)中定义了一个list容器connList,使用connList存放空闲的连接。
在对connList内的连接进行操作的时候,需要通过加锁来保证程序的安全性,所以头文件中定义了一个线程锁lock,通过使用lock保证同一时间只能有一个线程对容器connList进行操作。
由于连接池类ConnPool要统一管理系统中的所有连接,所以在整个系统中只需要维护一个连接池对象。如果系统中定义了多个连接池对象,那么每一个对象都可以建立maxSize个连接,在这种情况下使用连接池没有任何意义(与不使用连接池效果一样),也破环了通过连接池统一管理系统中连接的初衷,所以数据库连接池需要使用单例模式编写连接池类:单例模式确保一个类只有一个实例,该类自己进行实例化,并且向整个系统提供这个实例。
在头文件“connection_pool.h”中,我们定义了一个静态的连接池对象connPool ,连接池类ConnPool提供一个静态的公共方法GetInstance() ,外部程序通过调用这个方法来获得连接池对象。由于把连接池类ConnPool的构造函数定义为私有的,所以外部的应用程序不能够通过new的方式来实例化连接池类,只能通过GetInstance()方法获得连接池对象。在GetInstance()方法中,需要判断连接池类中定义的连接池对象connPool是否为“NULL”,若为“NULL”则调用私有构造函数实例化connPool;若不为“NULL”,则直接返回connPool,这样就实现了连接池类的单例模式,从而保证了系统运行过程中只建立一个连接池类的实例对象。
在实例化连接池类的对象connPool时,要对连接池做一些初始化操作,即建立一定数量的数据库连接。本文的程序通过“void InitConnection(int iInitialSize)”方法对连接池进行初始化,创建iInitialSize个连接,并且将这些连接放到连接池中的容器connList中,每新建一个连接,当前已建立的连接数curSize就加1。
当程序进行访问数据库时,需要从连接池中获取一个连接,本文是通过GetConnection()方法实现的,大致步骤如下:
1. 首先判断容器中是否还有空闲连接,如果有,则取出容器中的第一个连接,并且将该连接从容器中移除。判断上一步中获得的连接是否已经关闭,如果已关闭,则回收该连接的内存空间,并且重新创建一个连接,然后判断新创建的连接是否为空,如果为空,则需要将已经建立连接的数量减1,去掉这个空连接(也可以说是前面已经关闭的连接)。
2. 如果容器中已经没有空闲连接了,则要判断当前的curSize值是否已经达到规定的最大连接数maxSize:
连接使用完后,需要将该连接放回连接池中,本文通过“void ReleaseConnection(Connection *conn)”方法实现。该方法的具体实现就是将传进来的数据库连接(重新)添加到连接池的容器connList中。
当程序需要销毁数据库连接池(回收连接池的内存空间)时,需要先关闭、销毁连接池中的连接(回收连接池中所有连接的内存空间),然后再释放连接池对象的内存空间(即容器的clear操作)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。