数据库连接池

对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时,系统首先检查空闲池内有没有空闲连接。如果有就把建立时间最长(通过容器的顺序存放实现)的那个连接分配给他(实际是先做连接是否有效的判断,如果可用就分配给用户,如不可用就把这个连接从空闲池删掉,重新检测空闲池是否还有连接);如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数(maxConn),如果没有达到,就新建一个连接,如果已经达到,就等待一定的时间(timeout)。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户,如果等待时间超过预定时间timeout,则返回空值(null)。系统对已经分配出去正在使用的连接只做计数,当使用完后再返还给空闲池。对于空闲连接的状态,可开辟专门的线程定时检测,这样会花费一定的系统开销,但可以保证较快的响应速度。也可采取不开辟专门线程,只是在分配前检测的方法。 

连接池的实现 
  1、连接池模型 
  本文讨论的连接池包括一个连接池类(DBConnectionPool)和一个连接池管理类(DBConnetionPoolManager)。连接池类是对某一数据库所有连接的“缓冲池”,主要实现以下功能:①从连接池获取或创建可用连接;②使用完毕之后,把连接返还给连接池;③在系统关闭前,断开所有连接并释放连接占用的系统资源;④还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题),并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。 
  连接池管理类是连接池类的外覆类(wrapper),符合单例模式,即系统中只能有一个连接池管理类的实例。其主要用于对多个连接池对象的管理,具有以下功能:①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息,创建连接池对象;③为方便管理多个连接池对象,为每一个连接池对象取一个名字,实现连接池名字与其实例之间的映射;④跟踪客户使用连接情况,以便需要是关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理,如系统需要连接不同的数据库,或连接相同的数据库但由于安全性问题,需要不同的用户使用不同的名称和密码。

 

2、连接池实现 

class CManageConnection

{

private:

  float m_CreateTime;

ConnectionPtr   m_conn;

Bool  m_IsConnection;

 public:

   CManageConnection(float intime,Cstring strConn);

  ~ CManageConnection();

   Bool IsConnection(); //是否空闲

   Bool IsFreeConnection();//是否还是可用连接

   ConnectionPtr GetConnection();

  Private:

   RealseConn();


}

class CmanagePoolConn

{

 private:

int  minConn;//连接池里连接的最小数量 

  int  maxConn;//连接池里允许存在的最大连接数 

int  checkedOut;//已被分配出去的连接数

Cstring m_strConn;//连接字符串

Timer  timer;//定时器 

CArray< CManageConnection *, CManageConnection *> m_A ManageConn;


 

Public :

CmanagePoolConn();

~ CmanagePoolConn();

 

public:

  void CreateManagePool(int innum1,int innum2,Cstring instr);

  void FreeManagePool();

 

  _ConntionPtr * GetConnection ();

private :

  void  TimerEvent()  //定时器事件处理函数 

   CManageConnection *NewManageConnection();//新建一个连接

   Void DelectOldConnection(int index);//删除无用连接

}


二,一个数据库连接池实现

// ConnObject.h: interface for the CConnObject class.
//
//

#if !defined(AFX_CONNOBJECT_H__2818382C_8737_44D0_B319_1032C614EFEE__INCLUDED_)
#define AFX_CONNOBJECT_H__2818382C_8737_44D0_B319_1032C614EFEE__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "Database.h"

class CConnPool;

class CConnObject
{
public:
 CConnObject(CConnPool *  pool)
 {
  ASSERT(pool);
  pPool = pool;
 // conn = NULL;
  m_bInUse = FALSE;
  m_nLastEnd = 0;
  m_nLastBegin = 0;
  m_nUseCount   = 0;
  m_nTotalUseTime = 0;
 }
 ~CConnObject();
 // 用完本身不能关闭,交给连接池处理
 


public:
 //释放连接对象给连接池
 void Close();

 Database          conn;
 BOOL     m_bInUse;
 int      m_nLastEnd;   //最后一次使用结束时间
 int      m_nLastBegin;    //最后一次开始时间
 int      m_nUseCount;     //使用次数
 CConnPool *       pPool;
 int      m_nTotalUseTime;

public:

 //得到总得使用时间
 int  GetTotalUseTime()
 {
  return m_nTotalUseTime;
 }
};
CConnObject::~CConnObject()
{

}

void CConnObject::Close()
{
 m_nTotalUseTime += GetTickCount() - m_nLastBegin;
 pPool->Close(this);
}

#endif //
// ConnPool.h: interface for the CConnPool class.
//
//

#if !defined(AFX_CONNPOOL_H__FD09CC72_3241_4A82_912F_06F92BE1CD54__INCLUDED_)
#define AFX_CONNPOOL_H__FD09CC72_3241_4A82_912F_06F92BE1CD54__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
 
#include <atlbase.h>

#include "ConnObject.h"
 
/***************************************
数据库连接池.
一个连接池对应于一个库
****************************************/

/*

*/
class CConnPool  
{
public:
 void OnTimer();
 static void __stdcall TimerProc(HWND hWnd, UINT msg, UINT id, DWORD dwTime);

 //得到一个连接
 CConnObject * GetConnect();
 //释放一个连接回连接池
 void Close(CConnObject * pConn);
 //销毁连接池
 void Destroy();
 //创建连接池  strConn: 连接串
 BOOL Init(CString strConn);

 CConnPool();
 virtual ~CConnPool();


public:

 UINT        m_nTimer;     //定时器
 
 CSimpleArray<CConnObject *>      m_listConn;   //连接链表

 UINT        m_nTimeOut;   //连接最大空闲时间
 UINT        m_ConnMax;    //连接数量上限
 UINT        m_ConnMin;    //连接数量下限
 UINT        m_nMaxCount;  //连接最大使用次数
 UINT        m_nMaxUseTime; //最大使用时间
 CString        m_strConn;    //连接串

 HANDLE                 m_cs;   //保证改变连接池状态时代码不终端

protected:

 //创建一个新的连接
 CConnObject * CreateNewConn();
};

#endif // !defined(AFX_CONNPOOL_H__FD09CC72_3241_4A82_912F_06F92BE1CD54__INCLUDED_)
// ConnPool.cpp: implementation of the CConnPool class.
//
//

#include "stdafx.h"
#include "HTRDServer.h"
#include "ConnPool.h"
#include "const_def.h"
#include "MyEvent.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//
// Construction/Destruction
//


CConnPool::CConnPool()
{

 m_ConnMin = CONN_MIN;
 m_nTimeOut = CONN_TIMEOUT;
 m_nMaxUseTime = CONN_MAX_USETIME;
 m_nMaxCount  =  500;

 m_cs = CreateEvent(NULL, TRUE, TRUE, "da8b4c9d-e4ef-4c9a-a6d2-941ee98400d2");

}

CConnPool::~CConnPool()
{
 Destroy();
 
}

BOOL CConnPool::Init(CString strConn)
{
 m_ConnMax = theApp.m_sysSetting.m_nConnCount;

 m_strConn = strConn;
 for (UINT i = 0;i < m_ConnMax; i++)
 {
  CConnObject * pObj = CreateNewConn();
  if (!pObj)
   return FALSE;
  m_listConn.Add(pObj);
 }
 
 return TRUE;

 //启动连接池维护计时器
// m_nTimer = SetTimer(0, 0, CONNPOOL_TIMER, (TIMERPROC)TimerProc);

}

void CConnPool::Destroy()
{
 for (int i = 0;i < m_listConn.GetSize(); i++)
 {
  m_listConn[i]->m_bInUse = FALSE;

   m_listConn[i]->conn.Close();
 // m_listConn[i]->conn->Release();

  delete   m_listConn[i];
 }

 m_listConn.RemoveAll();

 KillTimer(NULL, m_nTimer);
}
/*
 * 释放连接时需要保护数据一致性,所以使用 临界对象
 */
void CConnPool::Close(CConnObject *pConn)
{
 
 //回收!

 CMyEvent cs(m_cs);

 for (int i = 0;i < m_listConn.GetSize(); i++)
 {
  if (m_listConn[i] == pConn)
  {
   m_listConn[i]->m_bInUse = FALSE;
   m_listConn[i]->m_nUseCount++;
   m_listConn[i]->m_nLastEnd = ::GetTickCount();

   //判断使用次数是否过多,使用时间是否过多
   //如果过度使用该连接,则重新创建
   if (m_listConn[i]->m_nUseCount > (int)m_nMaxCount ||
    m_listConn[i]->GetTotalUseTime() > CONN_MAX_USETIME)
   {
    OUTPUT_MSG("有数据库连接对象使用过多,需要重新连接/r/n");
    
    m_listConn[i]->conn.Close();
    if (!m_listConn[i]->conn.Open("", "", m_strConn.GetBuffer(0)))
    {
     AfxMessageBox("重新数据库连接对象出错!, 系统退出!");
     ExitProcess(0);
    }
    m_listConn[i]->m_nLastBegin = 0;
    m_listConn[i]->m_nLastEnd = 0;
    m_listConn[i]->m_nTotalUseTime = 0;
    m_listConn[i]->m_nUseCount = 0;

   }
   
   return;
  }
   
 }


#ifdef _DEBUG
 AfxMessageBox("The connect object is no mine!");
#endif

}
/*
 * 得到连接时需要保护数据一致性,所以使用 临界对象
 */
CConnObject * CConnPool::GetConnect()
{
 CMyEvent cs(m_cs);

 CConnObject * pObj = NULL;
 for (int i = 0;i < m_listConn.GetSize(); i++)
 {
  if (!m_listConn[i]->m_bInUse)
  {
   m_listConn[i]->m_bInUse = TRUE;
   pObj = m_listConn[i];
   pObj->m_nLastBegin = ::GetTickCount();
   
   return pObj;
  }
   
 }

 if ((pObj == NULL) && 
  (m_listConn.GetSize() < (int)m_ConnMax)) //connect object is no enough!
 {
  pObj = CreateNewConn();
  pObj->m_bInUse = TRUE;
  m_listConn.Add(pObj);
  return pObj;
 }


 //连接池已经满载!
 return NULL;
}

CConnObject * CConnPool::CreateNewConn()
{
 CConnObject * pConn = new CConnObject(this);
 
 
 if (!pConn->conn.Open("","",_bstr_t(m_strConn)))
  return NULL;
 
 
 return pConn;
}

void CConnPool::TimerProc(HWND hWnd, UINT msg, UINT id, DWORD dwTime)
{
// CHTRDServerApp * pApp = (CHTRDServerApp *)AfxGetApp();
// ASSERT(pPool);

// pPool->OnTimer();
 
}

/*
 * 调度连接池
 */
void CConnPool::OnTimer()
{  
 
 DWORD dwTime = ::GetTickCount();
 for (int i = 0; i < m_listConn.GetSize(); i++)
 {
  if (m_listConn[i]->conn.m_Cnn->State == adStateClosed)
  {
   //delete from pool
   delete m_listConn[i];
   m_listConn.RemoveAt(i);
   TRACE("One conn closed!, delete from pool!/n");
  }
  else if (!m_listConn[i]->m_bInUse)
  {
   if (dwTime - m_listConn[i]->m_nLastEnd >= m_nTimeOut)
   {
    m_listConn[i]->conn.Close();
    delete m_listConn[i];
    m_listConn.RemoveAt(i);
    TRACE("One conn time out!/n");
   }
   else if (m_listConn[i]->m_nUseCount >= (int)m_nMaxCount)
   {
    m_listConn[i]->conn.Close();
    delete m_listConn[i];
    m_listConn.RemoveAt(i);
    TRACE("One conn use to much!/n");

   }
  }
  else if (m_listConn[i]->GetTotalUseTime() >= (int)m_nMaxUseTime)
  {
   m_listConn[i]->conn.Close();
   delete m_listConn[i];
   m_listConn.RemoveAt(i);

   TRACE("One conn use too long/n");
  }
 }

 //检查连接数,小于下限则添加到下限
 for (i = m_listConn.GetSize(); i < (int)m_ConnMin;i++)
 {
  CConnObject * pObj = CreateNewConn();
  ASSERT(pObj);
  m_listConn.Add(pObj);
 }
 
}


三,在LINUX下用C/C++写了一个连接池(访问MYSQL)的类

一、头文件【存为:connPool.h】

#ifndef __CONNECTION_POOL_H__
#define __CONNECTION_POOL_H__

#include "mutex.h"


#define  MYSQL_CONN_NUM_MAX_VALUE   500
using namespace std;

enum _USE_STATUS
{
   US_USE = 0,
   US_IDLE = 1
};

typedef  struct _sConStatus
{
   void*  connAddr;
   int    useStatus;
}sConStatus;


class CConnPool
{
public:
    CConnPool();
    ~CConnPool();
public:
    int Init(string& strMysqlIp, string&  strUser, string&  strPwd, string&  strDbName, int nMysqlPort, int nConnNum);//connection  pool init
    void* getOneConn();//get a connection
    void  retOneConn(void* pMysql);// return a connection
    void  checkConn(); // check the connection if is alive
   
    void* createOneConn();
   
public:

   char m_szMysqlIp[100];
   char m_szUser[100];
   char m_szPwd[100];
   char m_szDbName[100];
   int  m_nMysqlPort;  
   int  m_nConnNum;
     
public:
    CMutex  m_sMutex;
    vector<void*>  m_vectorConn;
    map<void*, int> m_mapVI;
    map<void*, void*> m_mapMysqlScs;
   
};


 
class CConnPoolV2
{
public:
    CConnPoolV2();
    ~CConnPoolV2();
public:
    int Init(string& strMysqlIp, string&  strUser, string&  strPwd, string&  strDbName, int nMysqlPort, int nConnNum);//connection  pool init
    void* getOneConn(); //从连接池取一个连接
    void  retOneConn(void* pConn);// 连接用完了,把它放回连接池。以便其他人用。
    void  checkConn(); // check the connection if is alive
   
    void* createOneConn();
   
private:
   string m_strMysqlIp;
   string m_strUser;
   string m_strPwd;
   string m_strDbName;
   int  m_nMysqlPort; 
   int  m_nConnNum;
     
private:
    CMutex  m_sMutex;
    vector<void*>  m_vectorConn;
    map<void*, int> m_mapVI; //  从连接的地址,快速找到索引,便于存放到m_vectorConn中。
  
};
 

#endif

 

二、源码【存为:connPool.cpp】

 

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h> 
#include <iostream>                      
#include <memory>
#include <string>
#include <map>
#include <vector>

#include "mysql.h"
#include "encapsulation_mysql.h"
#include "connPool.h"
#include "mutex.h"

 

using namespace std;
using namespace EncapMysql;


CConnPool::CConnPool( )
{
}

CConnPool::~CConnPool( )
{
}

void* CConnPool::createOneConn()
{
     MYSQL*  mysql;
     mysql = mysql_init(0);
     if(mysql == NULL)
     {
    cout << "mysql_init fail**" << endl;     
    return NULL;
     }
 
     if(mysql_real_connect(mysql,  m_szMysqlIp , m_szUser ,   m_szPwd,    m_szDbName  , m_nMysqlPort, NULL,0)==NULL)
     {
     cout << "connect failure!" << endl;
     return NULL;
   }
     else
     {
    cout << "connect success!" << endl;
     }
     //
     return mysql;
 
}

int CConnPool::Init(string& strMysqlIp, string&  strUser, string&  strPwd, string&  strDbName, int nMysqlPort, int nConnNum)
{
 
    strcpy(m_szMysqlIp, strMysqlIp.c_str());
    strcpy( m_szUser, strUser.c_str());
    strcpy(m_szPwd, strPwd.c_str());
    strcpy(m_szDbName, strDbName.c_str());
    m_nMysqlPort = nMysqlPort;  
   m_nConnNum = nConnNum; 
  
    MYSQL*  mysql;
   
    for(int i=0; i<nConnNum; i++)
    {
       mysql = (MYSQL*)this->createOneConn();
       if(mysql == NULL)
        return -1;
       // 
      sConStatus* scs = new sConStatus();
      scs->connAddr = mysql;
      scs->useStatus = US_IDLE;
      m_vectorConn.push_back(scs); 
      m_mapVI[scs] = i;
      m_mapMysqlScs[mysql] = scs;
  }
  
  m_nConnNum = nConnNum;
}

//从连接池中取一个连接,同时,给它做一个标记,表明它已经被使用,防止别的线程再使用。
void* CConnPool::getOneConn()
{
    int N = m_vectorConn.size();
  for(int i=0; i< N; i++)
  {
      CGuard  guard(m_sMutex);
      sConStatus* scs = (sConStatus*)m_vectorConn[i];
        if(scs->useStatus ==  US_IDLE)
        {
           scs->useStatus = US_USE;
          return  scs->connAddr;
        } 
  }
  //
  return NULL;
}

//把连接归还给连接池。同时,给它做一个标记,表明它是空闲的,可以使用。
void  CConnPool::retOneConn(void* pMysql)
{
  if(!pMysql)
    return;
  // 
  map<void*, void*>::iterator  it1;
  map<void*, int>::iterator it2;
 
  CGuard  guard(m_sMutex);
 
  it1 = m_mapMysqlScs.find(pMysql);
  if(it1 == m_mapMysqlScs.end())
      return;
  it2 = m_mapVI.find(it1->second);
  if(it2 == m_mapVI.end())
      return;
  int nInx = it2->second;

  sConStatus* scs = (sConStatus*) m_vectorConn[nInx];
  scs->useStatus = US_IDLE;
  
}


void  CConnPool::checkConn()
{
   map<void*, void*>::iterator  it1;
   MYSQL*  mysql;
   // 
    for(int i=0; i<m_nConnNum ; i++)
    {
     CGuard  guard(m_sMutex);
     sConStatus* scs = (sConStatus*)m_vectorConn[i];
     if(scs->useStatus == US_USE)
         continue;
     //   
     mysql =(MYSQL*)(scs->connAddr);
     int status=mysql_query(mysql, "select count(*) from t_user;" );
    if(status != 0) //说明连接已经不可用了。
    {
        it1 = m_mapMysqlScs.find(mysql);
      if(it1 != m_mapMysqlScs.end())
       {
          m_mapMysqlScs.erase(it1);
       }
      //
        mysql_close(mysql);
        //
        mysql = (MYSQL*)this->createOneConn();
      m_mapMysqlScs[mysql] = scs;
    }
    }
    //
}

 
// 2011-01-20, 这个类这样写,感觉耦合性更为松散,比较好。使用起来也好理解一些。
CConnPoolV2::CConnPoolV2( )
{
}

CConnPoolV2::~CConnPoolV2( )
{
}

//创建一个连接,并设为 IDLE状态。
void* CConnPoolV2::createOneConn()
{
   try
   {
     CEncapMysql*  pEM = new CEncapMysql();
     if(pEM == NULL)
     {
      printf("pEM == NULL**\r\n"); 
      return NULL;
     }   
     //
     int nRet = pEM->Connect(m_strMysqlIp.c_str(), m_strUser.c_str(), m_strPwd.c_str());
     if(nRet != 0)
     {
      printf("pEM->Connect fail**\r\n"); 
      return NULL;
     }         
        //      
      pEM->SetIdle();
     //
     return pEM;
   
  catch(...)
  {
     printf("createOneConn  exception**\r\n"); 
     return NULL;
  }
}

//成功: 返回0
int CConnPoolV2::Init(string& strMysqlIp, string&  strUser, string&  strPwd, string&  strDbName, int nMysqlPort, int nConnNum)
{
 
    m_strMysqlIp  = strMysqlIp;
    m_strUser     = strUser;
    m_strPwd      = strPwd;
    m_strDbName   = strDbName;
    m_nMysqlPort = nMysqlPort;  
   m_nConnNum = nConnNum; 
  
    CEncapMysql* pEM;
    int nRet;
    for(int i=0; i<nConnNum; i++)
    {
       pEM = (CEncapMysql*)this->createOneConn();
        if(!pEM )
          return -1;
       // 
      m_vectorConn.push_back(pEM); 
      m_mapVI[pEM] = i;
  }
  
  return  0;
}


void* CConnPoolV2::getOneConn()
{
    CGuard  guard(m_sMutex);
    //
  for(int i=0; i< m_nConnNum; i++)
  {

      CEncapMysql* pEM = (CEncapMysql*)m_vectorConn[i];
        if( pEM->IsIdle())
        {
           pEM->SetUsed();
          return pEM;
        } 
  }
  //可能访问MYSQL的用户较多,连接池中已无空闲连接了。 只要总连接数没有超限,就新建一个连接。
  if(m_nConnNum < MYSQL_CONN_NUM_MAX_VALUE)
  {
     CEncapMysql* pEM = (CEncapMysql*)this->createOneConn();
        if(!pEM )
          return NULL;
       // 
      m_vectorConn.push_back(pEM); 
      m_mapVI[pEM] = m_nConnNum++;
  
  //
  return NULL;
}

void  CConnPoolV2::retOneConn(void* pConn)
{
  map<void*, int>::iterator it;
 
  CGuard  guard(m_sMutex);

  it = m_mapVI.find(pConn);
  if(it == m_mapVI.end())
  {
      printf("retOneConn  fail***\n"); 
      return;
  }   
  int nInx = it->second;

  CEncapMysql* pEM = (CEncapMysql*) m_vectorConn[nInx];
  pEM->SetIdle();

   printf("retOneConn  succ!\n"); 
}


void  CConnPoolV2::checkConn()
{
   
    //暂时可以不实现。 因为查询失败时,已重新连接了。
}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值