1.鏈接池的介紹: html
1.1應用背景: mysql
通常的應用程序都會訪問到數據庫,在程序訪問數據庫的時候,每一次數據訪問請求都必須通過下面幾個步驟:創建數據庫鏈接,打開數據庫,對數據庫中的數據進行操做,關閉數據庫鏈接。而創建數據庫鏈接和打開數據庫是一件很消耗資源而且費時的工做,若是在系統中很頻繁的發生這種數據庫鏈接,必然會影響到系統的性能,甚至會致使系統的崩潰。 sql
1.2技術思想: 數據庫
在系統初始化階段,創建必定數量的數據庫鏈接對象(Connection),並將其存儲在鏈接池中定義的容器中。當有數據庫訪問請求時,就從鏈接池中的這個容器中拿出一個鏈接;當容器中的鏈接已經用完,而且尚未達到系統定義的最大鏈接數時,能夠再建立一個新的鏈接,噹噹前使用的鏈接數達到最大鏈接數時,就要等待其餘訪問請求將鏈接放回容器後才能使用。當使用完鏈接的時候,必須將鏈接放回容器中,這樣不一樣的數據庫訪問請求就能夠共享這些鏈接,經過重複使用這些已經創建的數據庫鏈接,能夠解決上節中說到的頻繁創建鏈接的缺點,從而提升了系統的性能。 編程
通過上述描述,咱們能夠概括出數據庫鏈接池的主要操做: 安全
(1)首先創建一個數據庫鏈接池對象 app
(2)初始化必定數量的數據庫鏈接,放入鏈接池對象的容器中 tcp
(3)當有數據庫訪問請求時,直接從鏈接池的容器中獲得一個鏈接,這裏出現三種狀況: 函數
(a)當容器中的還有鏈接時,則返回給數據庫訪問請求者一個鏈接 性能
(b)當容器中沒有鏈接時,而且當前創建的鏈接數沒有達到系統定義的最大鏈接數,則建立一個新的數據庫鏈接。
(c)當容器中的沒有鏈接而且當前創建的鏈接數達到系統定義的最大鏈接數,則當前訪問數據庫請求就要等待其餘訪問請求釋放鏈接。
(4)當數據庫訪問完成後,應該將鏈接放回鏈接池的容器中。
(5)當服務中止時,須要先釋放數據庫鏈接池中的全部數據庫鏈接,而後再釋放數據庫鏈接池對象。
2.編程實現:
頭文件(connection_pool.h):
- /*
- *File: connection_pool.h
- *Author: csc
- */
- #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>
- usingnamespace std;
- usingnamespace sql;
-
- classConnPool{
- private:
- int curSize;//當前已創建的數據庫鏈接數量
- int maxSize;//鏈接池中定義的最大數據庫鏈接數
- string username;
- string password;
- string url;
- list<Connection*> connList;//鏈接池的容器隊列
- pthread_mutex_tlock;//線程鎖
- staticConnPool * connPool;
- Driver* driver;
-
- Connection* CreateConnection();//建立一個鏈接
- void InitConnection(int iInitialSize);//初始化數據庫鏈接池
- void DestoryConnection(Connection *conn);//銷燬數據庫鏈接對象
- void DestoryConnPool();//銷燬數據庫鏈接池
- ConnPool(stringurl,string user,string password,int maxSize);//構造方法
- public:
- ~ConnPool();
- Connection*GetConnection();//得到數據庫鏈接
- void ReleaseConnection(Connection *conn);//將數據庫鏈接放回到鏈接池的容器中
- static ConnPool *GetInstance();//獲取數據庫鏈接池對象
- };
- #endif /*_CONNECTION_POOL_H */
頭文件中定義了一個容器connList,裏面存放了不少個未使用的鏈接;在對容器內的鏈接進行操做的時候,須要加鎖來保證程序的安全性,因此頭文件中定義了一個lock,經過使用lock保證了同一時間只有一個線程對容器進行操做。
鏈接池類要統一管理整個應用程序中的鏈接,因此在整個系統中只須要維護一個鏈接池對象,試想:若是系統中定義了多個鏈接池對象,那麼每個對象均可以創建maxSize個鏈接,這樣就失去了建立鏈接池的初衷,破環了經過鏈接池統一管理系統中鏈接的思想。因此這裏使用單例模式編寫鏈接池類,單例模式確保一個類只有一個實例,本身進行實例化而且向整個系統提供這個實例。在頭文件中,咱們定義了一個靜態的鏈接池對象connPool,鏈接池類提供一個靜態的公共方法GetInstance(),外部程序經過調用這個方法來得到鏈接池對象。而且將鏈接池類的構造函數定義爲私有的,外部的應用程序不可以經過new來實例化鏈接池類,只能經過GetInstance()方法得到鏈接池對象;在GetInstance()方法中須要判斷鏈接池類中定義的connPool是否爲NULL,若爲NULL則調用私有構造函數實例化connPool,若不爲空,則直接返回connPool。這樣就實現了鏈接池類的單例模式,從而保證了系統運行過程當中只創建一個鏈接池類的實例對象。
在實例化鏈接池類的對象時,要對鏈接池作一些初始化的操做,即創建必定數量的數據庫鏈接。程序中經過InitConnection(intiInitialSize)方法對鏈接池進行初始化,建立iInitialSize個鏈接,而且將這些鏈接放在鏈接池中的容器connList中,每新建一個鏈接,curSize就加1。當有數據庫訪問請求時,須要從鏈接池中獲取一個鏈接,經過GetConnection()方法實現:首先判斷容器中是否還有鏈接,若是有,則拿出容器中的第一個鏈接,而且將該鏈接移出容器;得到的鏈接要進行判斷,若是鏈接已經關閉,則回收該鏈接的內存空間,而且從新建立一個鏈接;而後判斷新建立的鏈接是否爲空,若是爲空,則說明當前已經創建鏈接的數量並非curSize個,而是(curSize-1)個(應該除去這個空鏈接)。若是容器中已經沒有鏈接了,則要判斷當前的curSize值是否已經達到規定的maxSize,若是沒有小於maxSize,將創建一個新的鏈接(++curSize)。若是超過maxSize則等待其餘數據庫訪問請求釋放數據庫鏈接。
鏈接使用完之後,須要將鏈接放回鏈接池中,經過ReleaseConnection(sql::Connection* conn)方法實現,它的實現很是簡單,就是將傳進來的connection鏈接添加到鏈接池的容器中。
當須要回收鏈接池的內存空間時,須要先回收鏈接池中全部鏈接的內存空間,而後再釋放鏈接池對象的內存空間。
實現數據庫鏈接池主要的步驟就是上述這些,具體的代碼實現以下所示:
- #include<stdexcept>
- #include<exception>
- #include<stdio.h>
- #include"connection_pool.h"
-
- usingnamespace std;
- usingnamespace sql;
-
- ConnPool*ConnPool::connPool=NULL;
- //鏈接池的構造函數
- ConnPool::ConnPool(stringurl, 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("驅動鏈接出錯;\n");
- }
- catch(std::runtime_error&e)
- {
- perror("運行出錯了\n");
- }
- this->InitConnection(maxSize/2);
- }
- //獲取鏈接池對象,單例模式
- ConnPool*ConnPool::GetInstance(){
- if(connPool==NULL)
- {
- connPool=newConnPool("tcp://127.0.0.1:3306","root","root",50);
- }
- returnconnPool;
- }
- //初始化鏈接池,建立最大鏈接數的一半鏈接數量
- voidConnPool::InitConnection(int iInitialSize)
- {
- Connection*conn;
- pthread_mutex_lock(&lock);
- for(inti=0;i<iInitialSize;i++)
- {
- conn=this->CreateConnection();
- if(conn){
- connList.push_back(conn);
- ++(this->curSize);
- }
- else
- {
- perror("建立CONNECTION出錯");
- }
- }
- pthread_mutex_unlock(&lock);
- }
- //建立鏈接,返回一個Connection
- Connection*ConnPool::CreateConnection(){
- Connection*conn;
- try{
- conn=driver->connect(this->url,this->username,this->password);//創建鏈接
- returnconn;
- }
- catch(sql::SQLException&e)
- {
- perror("建立鏈接出錯");
- returnNULL;
- }
- catch(std::runtime_error&e)
- {
- perror("運行時出錯");
- returnNULL;
- }
- }
- //在鏈接池中得到一個鏈接
- Connection*ConnPool::GetConnection(){
- Connection*con;
- pthread_mutex_lock(&lock);
- if(connList.size()>0)//鏈接池容器中還有鏈接
- {
- con=connList.front();//獲得第一個鏈接
- connList.pop_front();//移除第一個鏈接
- if(con->isClosed())//若是鏈接已經被關閉,刪除後從新創建一個
- {
- deletecon;
- con=this->CreateConnection();
- }
- //若是鏈接爲空,則建立鏈接出錯
- if(con==NULL)
- {
- --curSize;
- }
- pthread_mutex_unlock(&lock);
- returncon;
- }
- else{
- if(curSize< maxSize){//還能夠建立新的鏈接
- con= this->CreateConnection();
- if(con){
- ++curSize;
- pthread_mutex_unlock(&lock);
- returncon;
- }
- else{
- pthread_mutex_unlock(&lock);
- returnNULL;
- }
- }
- else{//創建的鏈接數已經達到maxSize
- pthread_mutex_unlock(&lock);
- returnNULL;
- }
- }
- }
- //回收數據庫鏈接
- voidConnPool::ReleaseConnection(sql::Connection * conn){
- if(conn){
- pthread_mutex_lock(&lock);
- connList.push_back(conn);
- pthread_mutex_unlock(&lock);
- }
- }
- //鏈接池的析構函數
- ConnPool::~ConnPool()
- {
- this->DestoryConnPool();
- }
- //銷燬鏈接池,首先要先銷燬鏈接池的中鏈接
- voidConnPool::DestoryConnPool(){
- list<Connection*>::iterator icon;
- pthread_mutex_lock(&lock);
- for(icon=connList.begin();icon!=connList.end();++icon)
- {
- this->DestoryConnection(*icon);//銷燬鏈接池中的鏈接
- }
- curSize=0;
- connList.clear();//清空鏈接池中的鏈接
- pthread_mutex_unlock(&lock);
- }
- //銷燬一個鏈接
- voidConnPool::DestoryConnection(Connection* conn)
- {
- if(conn)
- {
- try{
- conn->close();
- }
- catch(sql::SQLException&e)
- {
- perror(e.what());
- }
- catch(std::exception&e)
- {
- perror(e.what());
- }
- deleteconn;
- }
- }