天天看點

使用boost::circular_buffer_space_optimized實作OTL資料庫連接配接池

引言

資料庫連接配接是一種關鍵的有限的昂貴的資源,是以對資料庫連接配接的管理能顯著影響到整個應用程式的伸縮性和健壯性,影響到程式的性能名額。資料庫連接配接池負責配置設定、管理和釋放資料庫連接配接,它允許應用程式重複使用一個現有的資料庫連接配接,而再不是重建立立一個;釋放空閑時間超過最大空閑時間的資料庫連接配接來避免因為沒有釋放資料庫連接配接而引起的資料庫連接配接遺漏。這項技術能明顯提高對資料庫操作的性能。

資料庫連接配接池的關鍵因素

1.容器

資料庫連接配接池是資料庫連接配接的集合,必須要有容器來存儲這些連接配接,許多方法都是用stl的list、queue、vector常用的容器來實作的,但今天我選擇了circular_buffer_space_optimized來實作,具體介紹可到www.boost.org上檢視。

2.最小連接配接數

資料庫連接配接池在初始化時将建立一定數量的資料庫連接配接放到連接配接池中,這些資料庫連接配接的數量是由最小資料庫連接配接數來設定的。無論這些資料庫連接配接是否被使用,連接配接池都将一直保證至少擁有這麼多的連接配接數量。

3.最大連接配接數

連接配接池的最大資料庫連接配接數量限定了這個連接配接池能占有的最大連接配接數,當應用程式向連接配接池請求的連接配接數超過最大連接配接數量時,這些請求将被加入到等待隊列中。

4.連接配接的使用

最先的連接配接請求将會獲利,之後超過最小連接配接數量的連接配接請求等價于建立一個新的資料庫連接配接。不過,這些大于最小連接配接數的資料庫連接配接在使用完不會馬上被釋放,它将被放到連接配接池中等待重複使用或是空閑逾時後被釋放。獲得連接配接的請求使用完之後,要把連接配接放回容器當中,以便之後的請求重複使用,達到重複利用的目的。

實作代碼

當然,我也不完全按照上面的主要因素來實作的,可根據circular_buffer_space_optimized和OTL的特性實作了一個簡單的資料庫連接配接池。

//資料庫連接配接池虛類db_conn_pool.h

#pragma once

#include <iostream>
#include <boost/circular_buffer.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>

template <class conn>
class db_conn_pool
{
public:
	typedef conn connection;
	typedef boost::shared_ptr<conn> connection_ptr;

	virtual connection_ptr getConnection() = 0;
	virtual void initConnection() = 0;
	int size(){return m_conn_container.size();}
	
protected:
	std::string m_conn_str;
	unsigned short m_max_size;

	boost::circular_buffer_space_optimized<connection_ptr> m_conn_container;
	boost::mutex m_mutex;

	db_conn_pool(){}
	db_conn_pool(const std::string& conn_str, unsigned short max_size)
		:m_conn_str(conn_str), m_max_size(max_size)
	{

	}

	virtual connection_ptr createConnection() = 0;
};

template<class conn_pool, class conn>
class connection_wrap{
public:
	connection_wrap(conn_pool& pool, boost::shared_ptr<conn> ptrConn)
		:m_pool(pool), m_ptrConn(ptrConn)
	{

	}

	~connection_wrap()
	{
		m_pool.releaseConnection(m_ptrConn);
	}

private:
	boost::shared_ptr<conn> m_ptrConn;
	conn_pool& m_pool;
};
           

//mysql資料庫連接配接池my_conn_pool.h

#pragma once
#include <iostream>
#define OTL_ODBC // CompileOTL 4.0/ODBC  
// Thefollowing #define is required with MyODBC 5.1 and higher  
#define OTL_ODBC_SELECT_STM_EXECUTE_BEFORE_DESCRIBE  
#define OTL_STL
#include "otlv4.h"
#include "db_conn_pool.h"
#include <boost/format.hpp>
#include <firebird/log/logger_log4.hpp>

//FUNC DEFINE
#define OTL_STREAM 						_otl_stream
#define OTL_EOF 						_otl_stream.eof()
#define OTL_SUCCEED						0						
#define OTL_FAILED						-1
#define	OTL_NO_DATA_FOUND				1403	
#define OTL_BEGIN(conn, sql)				    \
	try {											\
		LOG4CXX_DEBUG(console_log, KDS_CODE_INFO << sql);		\
		otl_stream _otl_stream(100, sql, *conn);           

#define OTL_END(conn, _count)	\
		_otl_stream.flush();							    	\
		_count = _otl_stream.get_rpc();		                	\
	} catch (otl_exception& otl_exp) { 						    \
		print_exception(otl_exp); 				    \
		if(otl_exp.code == 2006)					\
		{											\
			conn->logoff();							\
		}											\
		throw business_error(otl_exp.code, (char*)(otl_exp.msg));	\
	}

#define OTL_COMMIT(conn) conn->commit();

#define OTL_ROLLBACK(conn) conn->rollback();


static void print_exception(otl_exception& otl_exp)
{
	boost::format fmt("otl with exception:[%1%][%2%][%3%]");
	std::string str = boost::str(fmt 
		% otl_exp.code
		% otl_exp.msg
		% otl_exp.stm_text);
	LOG4CXX_ERROR(console_log, KDS_CODE_INFO << str);
}

class mysql_conn_pool : public db_conn_pool<otl_connect>
{
public:
	~mysql_conn_pool();
	static mysql_conn_pool& getInstance();
	void setParam(const std::string& conn_str, unsigned short max_size);

	connection_ptr getConnection();
	void releaseConnection(connection_ptr ptrConn);
	void initConnection();

protected:
	typedef db_conn_pool<otl_connect> super;
	mysql_conn_pool();
private:
	connection_ptr createConnection();
};
           

//my_conn_pool.cpp

#include "mysql_conn_pool.h"
#include <boost/typeof/typeof.hpp>

mysql_conn_pool::mysql_conn_pool()
{

}

mysql_conn_pool::~mysql_conn_pool()
{

}

mysql_conn_pool& mysql_conn_pool::getInstance()
{
	static mysql_conn_pool pool;
	return pool;
}

void mysql_conn_pool::setParam(const std::string& conn_str, unsigned short max_size)
{
	m_conn_str = conn_str;
	m_max_size = max_size;
}

void mysql_conn_pool::initConnection()
{
	m_conn_container.resize(m_max_size);
	otl_connect::otl_initialize(1); // initialize the database API environment

	for (int i = 0; i < m_max_size; ++i)
	{
		createConnection();
	}
}

mysql_conn_pool::connection_ptr mysql_conn_pool::getConnection()
{
	connection_ptr ptrConn;

	std::time_t begin;
	std::time(&begin);

	while(1)
	{
		boost::mutex::scoped_lock lock(m_mutex);

		if (m_conn_container.size() == 0)
		{
			std::time_t now;
			std::time(&now);
			if (now - begin > 10)
			{
				/*
				*若超過10秒還沒取得連接配接對象,則認為連接配接池裡的連接配接都失效用完,
				*應重新建立
				*/
				createConnection();
				begin = now;
			}
			continue;
		}

		ptrConn = m_conn_container.front();
		m_conn_container.pop_front();

		if (ptrConn != NULL && ptrConn->connected)
		{
			/*BOOST_AUTO(pos, m_conn_container.begin());
			m_conn_container.rotate(++pos);*/

			break;
		}
		else
		{
			//m_conn_container.pop_front();
			createConnection();
			continue;;
		}
	}

	return ptrConn;
}

mysql_conn_pool::connection_ptr mysql_conn_pool::createConnection()
{
	connection_ptr ptrConn(new otl_connect());
	ptrConn->rlogon(m_conn_str.c_str());

	if (ptrConn != NULL && ptrConn->connected)
	{
		ptrConn->auto_commit_on();
		ptrConn->set_timeout(60);
		m_conn_container.push_back(ptrConn);
	}

	return ptrConn;
}

void mysql_conn_pool::releaseConnection(connection_ptr ptrConn)
{
	boost::mutex::scoped_lock lock(m_mutex);
	if (ptrConn != NULL  && ptrConn->connected)
	{
		m_conn_container.push_back(ptrConn);
	}
}
           

//測試test.cpp

#include <iostream>
#include "my_conn_pool.h"

int main()
{
    mysql_conn_pool& m_mysql_pool = mysql_conn_pool::getInstance();
    m_mysql_pool.setParam("Driver={MySQL ODBC 5.2w Driver};Server=127.0.0.1;Port=3306;Database=opensips;Uid=root;Pwd=123321", 10);
    m_mysql_pool.initConnection();
    mysql_conn_pool::connection_ptr pConn = m_mysql_pool.getConnection();
    connection_wrap<mysql_conn_pool, mysql_conn_pool::connection> wrap(m_mysql_pool, pConn);
    std::string sql;
	sql = "delete from account_bind where ext_acct = :f1<char[50]>";

	int count = 0;
	OTL_BEGIN(pConn, sql.c_str());
	OTL_STREAM << req.sExtAcct;
	OTL_END(pConn, count);
    return 0;
}
           

繼續閱讀