From 76949c0bf4e34f15a5f9b1c2b870477c7dbeb6b8 Mon Sep 17 00:00:00 2001
From: znone <glyc@sina.com.cn>
Date: Wed, 15 Feb 2017 12:15:36 +0000
Subject: [PATCH] 增加数据库连接池。

---
 include/qtl_sqlite.hpp        |   13 ++
 include/qtl_database_pool.hpp |  174 ++++++++++++++++++++++++++++++++++
 include/qtl_sqlite_pool.hpp   |   40 ++++++++
 test/test_sqlite.mak          |    2 
 include/qtl_mysql_pool.hpp    |   36 +++++++
 5 files changed, 264 insertions(+), 1 deletions(-)

diff --git a/include/qtl_database_pool.hpp b/include/qtl_database_pool.hpp
new file mode 100644
index 0000000..68e31c6
--- /dev/null
+++ b/include/qtl_database_pool.hpp
@@ -0,0 +1,174 @@
+#ifndef _QTL_DATABASE_POOL_H_
+#define _QTL_DATABASE_POOL_H_
+
+#include <memory>
+#include <vector>
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <chrono>
+#include <algorithm>
+
+namespace qtl
+{
+
+template<typename Database>
+class database_pool
+{
+public:
+	typedef Database value_type;
+	typedef std::shared_ptr<Database> pointer;
+
+	database_pool()
+		: m_trying_connection(false), m_stop_thread(false)
+	{
+	}
+
+	virtual ~database_pool()
+	{
+		if(m_background_thread.joinable())
+		{
+			m_stop_thread=true;
+			m_background_thread.join();
+		}
+		clear();
+	}
+
+	pointer get()
+	{
+		Database* db=popup();
+		if(db==NULL && m_trying_connection==false)
+			db=create_database();
+		return pointer(db, [this](Database* db) {
+			recovery(db);
+		});
+	}
+
+	bool test_alive()
+	{
+		if(m_databases.empty()) 
+			return false;
+		std::unique_lock<std::mutex> lock(m_pool_mutex);
+		auto it=m_databases.begin();
+		while(it!=m_databases.end())
+		{
+			Database* db=*it;
+			if(!db->is_alive())
+			{
+				delete db;
+				it=m_databases.erase(it);
+			}
+			else
+			{
+				++it;
+			}
+		}
+		if(m_databases.empty())
+		{
+			lock.unlock();
+			try_connect();
+			return false;
+		}
+		else return true;
+	}
+
+private:
+	std::vector<Database*> m_databases;
+	std::mutex m_pool_mutex;
+	std::atomic<bool> m_trying_connection;
+	std::thread m_background_thread;
+	bool m_stop_thread;
+
+	virtual bool open_database(Database& db)=0;
+	void recovery(Database* db)
+	{
+		if(db==NULL) return;
+		if(db->is_alive())
+		{
+			std::lock_guard<std::mutex> lock(m_pool_mutex);
+			m_databases.push_back(db);
+		}
+		else
+		{
+			delete db;
+			{
+				std::lock_guard<std::mutex> lock(m_pool_mutex);
+				clear();
+			}
+			try_connect();
+		}
+	}
+
+	Database* create_database()
+	{
+		Database* db=new Database;
+		if(open_database(*db))
+			return db;
+
+		{
+			std::lock_guard<std::mutex> lock(m_pool_mutex);
+			clear();
+		}
+		try_connect();
+		return NULL;
+	}
+
+	Database* popup()
+	{
+		Database* db=NULL;
+		std::lock_guard<std::mutex> lock(m_pool_mutex);
+		if(!m_databases.empty())
+		{
+			db=m_databases.back();
+			m_databases.pop_back();
+		}
+		return db;
+	}
+
+	void try_connect()
+	{
+		if(m_trying_connection)
+			return;
+
+		m_trying_connection=true;
+		try
+		{
+			m_background_thread=std::thread(&database_pool<Database>::background_connect, this);
+		}
+		catch (std::system_error&)
+		{
+			m_trying_connection=false;
+		}
+	}
+
+	void background_connect()
+	{
+		Database* db=NULL;
+		int interval=1;
+		while(db==NULL && m_stop_thread==false)
+		{
+			db=create_database();
+			if(db==NULL)
+			{
+				std::this_thread::sleep_for(std::chrono::seconds(interval));
+				if(interval<60) interval<<=1;
+			}
+		}
+		if(db)
+		{
+			recovery(db);
+		}
+		m_background_thread.detach();
+		m_trying_connection=false;
+	}
+
+	void clear()
+	{
+		std::for_each(m_databases.begin(), m_databases.end(), std::default_delete<Database>());
+		m_databases.clear();
+	}
+};
+
+}
+
+#endif //_QTL_DATABASE_POOL_H_
diff --git a/include/qtl_mysql_pool.hpp b/include/qtl_mysql_pool.hpp
new file mode 100644
index 0000000..236d625
--- /dev/null
+++ b/include/qtl_mysql_pool.hpp
@@ -0,0 +1,36 @@
+#ifndef _QTL_MYSQL_POOL_H_
+#define _QTL_MYSQL_POOL_H_
+
+#include "qtl_database_pool.hpp"
+#include "qtl_mysql.hpp"
+
+namespace qtl
+{
+
+namespace mysql
+{
+
+class database_pool : public qtl::database_pool<database>
+{
+public:
+	database_pool() : m_port(0) { }
+	virtual ~database_pool() { }
+	virtual bool open_database(database& db) override
+	{
+		db.charset_name("utf8");
+		return db.open(m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port);
+	}
+
+protected:
+	std::string m_host;
+	unsigned short m_port;
+	std::string m_database;
+	std::string m_user;
+	std::string m_password;
+};
+
+}
+
+}
+
+#endif //_QTL_MYSQL_POOL_H_
diff --git a/include/qtl_sqlite.hpp b/include/qtl_sqlite.hpp
index bda21ad..679f1f8 100644
--- a/include/qtl_sqlite.hpp
+++ b/include/qtl_sqlite.hpp
@@ -423,6 +423,19 @@
 	{
 		simple_execute("ROLLBACK TRANSACTION");
 	}
+
+	bool is_alive()
+	{
+#ifdef _WIN32
+		return true;
+#else
+		int has_moved=0;
+		int result=sqlite3_file_control(m_db, NULL, SQLITE_FCNTL_HAS_MOVED, &has_moved);
+		if(result!=SQLITE_OK)
+			throw sqlite::error(result);
+		return has_moved==0;
+#endif //_WIN32
+	}
 	const char* errmsg() const { return sqlite3_errmsg(m_db); }
 	int error() const { return sqlite3_errcode(m_db); }
 	uint64_t insert_id() { return sqlite3_last_insert_rowid(m_db); }
diff --git a/include/qtl_sqlite_pool.hpp b/include/qtl_sqlite_pool.hpp
new file mode 100644
index 0000000..17b9a86
--- /dev/null
+++ b/include/qtl_sqlite_pool.hpp
@@ -0,0 +1,40 @@
+#ifndef _QTL_SQLITE_POOL_H_
+#define _QTL_SQLITE_POOL_H_
+
+#include "qtl_sqlite.hpp"
+#include "qtl_database_pool.hpp"
+
+namespace qtl
+{
+
+namespace sqlite
+{
+
+class database_pool : public qtl::database_pool<database>
+{
+public:
+	database_pool() : m_flags(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { }
+	virtual bool open_database(database& db) override
+	{
+		try
+		{
+			db.open(m_filename.data(), m_flags);
+			return true;
+		}
+		catch (error& e)
+		{
+			return false;
+		}
+	}
+
+protected:
+	std::string m_filename;
+	int m_flags;
+};
+
+}
+
+}
+
+#endif //_QTL_SQLITE_POOL_H_
+
diff --git a/test/test_sqlite.mak b/test/test_sqlite.mak
index 808388b..5b9c555 100644
--- a/test/test_sqlite.mak
+++ b/test/test_sqlite.mak
@@ -5,7 +5,7 @@
 OBJ=TestSqlite.o sqlite3.o md5.o
 CFLAGS=-g -D_DEBUG -O2 -I. -I/usr/local/include 
 CXXFLAGS=-I../include -std=c++11
-LDFLAGS= -L/usr/local/lib -ldl -lcpptest -lpthread
+LDFLAGS= -L/usr/local/lib -pthread -ldl -lcpptest
 
 all : $(TARGET)
 

--
Gitblit v1.9.3