C++ 连接池(基类), 被继承后可以兼容Mysql连接池和Redis连接池

程序类型: C++连接池抽象类

适用平台: Linux

适用数据库: Mysql(亲测可用), Redis(亲测可用), Oracle(亲测可用)

适合读者: C/C++初学者学习交流使用

使用注意: 此类属于C++抽象类, 必须被继承后使用

简单介绍: 这段时间开发服务器涉及到redis和mysql数据库连接池, 在网上翻阅资料基本上都是针对性的,比如针对mysql设计的mysql连接池, 针对redis设计的redis连接池, 自己想了想, 都是连接池, 为啥不能写一个公用的方法呢 , 写一个抽象类, 供其他数据库连接池继承, 这样也就符合了C++的多态思想(这边纯属装个<B>, 高手请绕道)

思路介绍:

  1. 首先有连接池,必须先创造一个由若干个连接组成的连接池来管理, 并指定最大和最小连接数
  2. 连接池创造好了,当然要提供给外部获取和返还的方法
  3. 在外部调用过多时, 不够的需要创造新的, 这时候就会出现调用完成反还后面用不到, 连接池内部出现太多的空闲连接白消耗资源,这时候就需要管理者来管理这些冗余的连接
  4. 创造管理者子线程,用来定期监控空闲线程和定期清理那些未知原因导致的连接与数据库断开的连接

功能介绍:

1. 需要子类重写的受保护的纯虚函数(protected)

//创建一个连接:纯虚函数, 子类必须要实现
virtual void * createLink() = 0;
//销毁指定的连接:纯虚函数
virtual void v_destroyLink(void *link) = 0;
//检测连接是否可以用:通过一个不影响数据库的请求检测,比如Mysql的mysql_ping(MYSQL* con)
virtual bool isUsed(void *link) = 0;

[注意] 获得连接和反还连接用的都是void*, 此处是因为当前类为抽象类, 不是具体的数据库连接池, 所有的连接对象类型都未知, 调用如下:

//比如这是Redis的连接池伪代码
//实例继承连接池的Redis连接池对象
RedisPool redis;
//将获得的redis连接强转成Redis对象
Redis *red = (Redis*)redis.getLink();

2. 介绍公共成员函数

//构造函数
LinkPool();
//获得错误码
LinkPool::ErrOpt errNo();
//初始化连接池
//(minLinkNum:连接池最小连接数, maxLinkNum:连接池最大连接数,checkFreeLinkSecs:检查空闲链接秒数)
void createLinkPool(int minLinkNum, int maxLinkNum, int checkFreeLinkSecs = 10);
//销毁一个连接
void destroyLink(void *link);
//获得一个连接
void * getLink();
//返还一个连接到连接池
void backLink(void *link);
//销毁连接池:析构时自动调用
void destroyLinkPool();
//虚析构:目的是为了析构子类时,析构父类
virtual ~LinkPool();

3. 介绍私有成员变量

//线程锁:用来多线程操作的安全,使用静态方便调用
static pthread_rwlock_t *g_lock;
//连接池列表:当前连接池可用连接的链表
list<void*> m_linkList;
//忙碌的连接列表:已被取出的连接链表					
list<void*> m_busyLinkList;	
//最大连接数:连接池容纳的最大连接数			
int m_maxLinkNum = 0;
//最小连接数:连接池容纳的最小连接数					
int m_minLinkNum = 0;
//检查连接秒数:检查是否存在空闲连接的频率
int m_checkFreeLinkSecs = 0;
//存管理线程tid:管理者子线程id,用来销毁线程池时进行回收			
pthread_t m_adjust_tid;
//忙碌连接数:当前忙碌的连接数(已被取出的)
int m_busyLinkNum = 0;
//空闲链接数:当前空闲的连接数(未被取出的)
int m_freeLinkNum = 0;
//错误码:出错时进行赋值(枚举)					
LinkPool::ErrOpt m_errno;
//是否销毁:是否销毁,管理者线程执行无限循环的判断条件, 销毁则自动结束管理者线程	
bool m_isDestory = false;		

4. 错误类型(enum)

enum ErrOpt {
	NoErr = 0,							//没有错误
	ErrBusying = -100,					//达到最大连接数(繁忙)
	ErrConnectFailed,					//连接失败
};

5. 私有成员函数

//创建多个链接
bool createLinks(int n);
//销毁一个连接:是不是似曾相识, 这是内部使用的,内部实现不一样的
void priDestroyLink(void *link = nullptr);
//判断空闲连接的管理者线程回调:管理者子线程的线程回调函数,格式不能变动
static void *adjustFreeLinck(void *linkPool);

所有的头文件都在这了,是不是很简单呢, 当然有些漏掉的功能, 我写的只是最基本的功能, 大家可以去自动扩展, 具体的实现我就不一一介绍了, 下面是具体的代码部分大家到这儿复制去用:

LinkPool.h

/**
	*@name 连接池基类
	*@brief 提供给链接池继承的基类
	*@see 是一个抽象类
	*@author 打工的小兵
	*@date 2019-03-23
*/
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <string.h>
#include <string>
#include <pthread.h>

using namespace std;

#define LINK_POOL_DEL_LINKS 10				//空闲连接一次性删除的最大个数
#define LINK_POOL_CHECK_KEEP_ALIVE_SECS 3 //检测保持连接的秒数(一定秒数检测一次是否断开连接)

class LinkPool
{
public:
	enum ErrOpt {
		NoErr = 0,							//没有错误
		ErrBusying = -100,					//达到最大连接数(繁忙)
		ErrConnectFailed,					//连接失败
	};

private:
	static pthread_rwlock_t *g_lock;		//线程锁
	list<void*> m_linkList;					//连接池列表
	list<void*> m_busyLinkList;				//忙碌的连接列表(此处为了严谨性, 防止意外终止子进程导致的忙碌连接无法被回收)
	int m_maxLinkNum = 0;					//最大连接数
	int m_minLinkNum = 0;					//最小连接数
	int m_checkFreeLinkSecs = 0;			//检查连接秒数
	pthread_t m_adjust_tid;					//存管理线程tid
	bool m_isDestory = false;				//是否销毁
	int m_busyLinkNum = 0;					//忙碌连接数
	int m_freeLinkNum = 0;					//空闲链接数
	LinkPool::ErrOpt m_errno;				//错误码

public:
	/**
	  *@name 构造函数
	  *@brief 构造函数
	  *@param 函数参数
	  *@see 注意事项
	  *@return 返回值
	  *@date 2019-03-23
	*/
	LinkPool();
	/**
	  *@name 获得错误码
	  *@brief 获得错误码
	  *@param 函数参数
	  *@see 注意事项
	  *@return LinkPool::ErrOpt枚举:0表示无错误
	  *@date 2019-03-23
	*/
	LinkPool::ErrOpt errNo();
	/**
	  *@name 创建链接池
	  *@brief 初始化连接池
	  *@param minLinkNum:连接池最小连接数, maxLinkNum:连接池最大连接数,checkFreeLinkSecs:检查空闲链接秒数
	  *@see 构造后必须调用
	  *@return void
	  *@date 2019-03-23
	*/
	void createLinkPool(int minLinkNum, int maxLinkNum, int checkFreeLinkSecs = 10);
	/**
	  *@name 销毁链接
	  *@brief 销毁一个连接
	  *@param link[null]:从连接池中空闲的链接中销毁一个(内部使用), link[!null]:销毁指定的连接, isNeedLock:是否需要加锁(true:是)
	  *@return void
	  *@date 2019-03-23
	*/
	void destroyLink(void *link);
	/**
	  *@name 获得一个连接
	  *@brief 获得一个连接
	  *@param 函数参数
	  *@see 注意事项
	  *@return void*:连接指针
	  *@date 2019-03-23
	*/
	void * getLink();
	/**
	  *@name 返还一个连接到连接池
	  *@brief 返还一个连接到连接池
	  *@param link:连接指针
	  *@see 注意事项
	  *@return void
	  *@date 2019-03-23
	*/
	void backLink(void *link);
	/**
	  *@name 销毁连接池
	  *@brief 销毁连接池
	  *@see 析构函数自动调用
	  *@return void
	  *@date 2019-03-23
	*/
	void destroyLinkPool();
	virtual ~LinkPool();

protected:
	/**
	  *@name 创建连接
	  *@brief 创建一个连接
	  *@param 函数参数
	  *@see 纯虚函数
	  *@return void*[null]:创建失败, void*[!null]:创建成功
	  *@date 2019-03-23
	*/
	virtual void * createLink() = 0;
	/**
	  *@name 销毁一个连接
	  *@brief 销毁指定的链接
	  *@param link:链接指针
	  *@see 子类纯虚函数,直接销毁给定对象,无需加锁
	  *@return void
	  *@date 2019-03-23
	*/
	virtual void v_destroyLink(void *link) = 0;
	/**
	  *@name 检查连接是否可用
	  *@brief 检测连接是否可以用
	  *@param link:连接指针
	  *@see 注意事项
	  *@return bool[true]:连接正常, bool[false]:连接不正常
	  *@date 2019-03-23
	*/
	virtual bool isUsed(void *link) = 0;

private:
	/**
	  *@name 创建多个链接
	  *@brief 创建多个连接
	  *@param n:连接数
	  *@see 需要外部加锁保护	
	  *@return bool[true]:创建成功, bool[false]:创建失败,设置errno
	  *@date 2019-03-23
	*/
	bool createLinks(int n);
	/**
	  *@name 销毁一个连接
	  *@brief 销毁一个连接
	  *@param link:指定连接,默认值,则随机销毁一个
	  *@see 当参数link非空时, 默认不从m_linkList中取出(提供给管理者使用), 如需取出,从外部取出
	  *@return void
	  *@date 2019-03-24
	*/
	void priDestroyLink(void *link = nullptr);
	/**
	  *@name 判断空闲连接
	  *@brief 判断空闲连接的管理者线程回调
	  *@param linkPool:回调参数,当前连接池
	  *@see 回调函数,创建线程调用
	  *@return void
	  *@date 2019-03-23
	*/
	static void *adjustFreeLinck(void *linkPool);
};

LinkPool.cpp

#include "LinkPool.h"
#include <unistd.h>
#include <iostream>

pthread_rwlock_t *LinkPool::g_lock = nullptr;

LinkPool::LinkPool()
{
	g_lock = new pthread_rwlock_t;
	pthread_rwlock_init(g_lock, nullptr);
	m_errno = NoErr;
}

/*获得错误码*/
LinkPool::ErrOpt LinkPool::errNo()
{
	pthread_rwlock_rdlock(g_lock);
	LinkPool::ErrOpt err = m_errno;
	pthread_rwlock_unlock(g_lock);
	return err;
}

/*初始化连接池*/
void LinkPool::createLinkPool(int minLinkNum, int maxLinkNum, int checkFreeLinkSecs)
{
	m_checkFreeLinkSecs = checkFreeLinkSecs;
	m_minLinkNum = minLinkNum;
	m_maxLinkNum = maxLinkNum;
	//创建连接
	pthread_rwlock_wrlock(g_lock);
	createLinks(minLinkNum);
	pthread_rwlock_unlock(g_lock);
	//加入管理者线程
	pthread_create(&m_adjust_tid, NULL, adjustFreeLinck, (void*)this);
}

/*销毁一个连接*/
void LinkPool::priDestroyLink(void * link)
{
	void *links = link;
	if (!link) {
		links = m_linkList.front();
		m_linkList.pop_front();
	}
	v_destroyLink(links);
	cout << "销毁的连接是:[" << m_linkList.front() << "]" << endl;
	m_freeLinkNum--;
}

/*销毁一个连接*/
void LinkPool::destroyLink(void * link)
{
	if (link) {
		//外部调用,必须加锁
		pthread_rwlock_wrlock(g_lock);
		v_destroyLink(link);
		//忙碌连接数-1:因为外部调用的是已经获得连接
		m_busyLinkList.remove(link);
		m_busyLinkNum--;
		pthread_rwlock_unlock(g_lock);
	}
}

/*获得一个连接*/
void * LinkPool::getLink()
{
	pthread_rwlock_wrlock(g_lock);
	if (m_linkList.empty() && !createLinks(1)) {
		pthread_rwlock_unlock(g_lock);
		return nullptr;
	}
	auto link = m_linkList.front();
	m_linkList.pop_front();
	m_busyLinkList.push_back(link);
	--m_freeLinkNum;
	++m_busyLinkNum;
	cout << "获得一个连接:[" << link << "], 当前空闲连接数:" << m_freeLinkNum << " 当前忙碌连接数:" << m_busyLinkNum << endl;
	pthread_rwlock_unlock(g_lock);
	return link;
}

/*返还一个连接到连接池*/
void LinkPool::backLink(void * link)
{
	pthread_rwlock_wrlock(g_lock);
	m_linkList.push_back(link);
	m_busyLinkList.remove(link);
	++m_freeLinkNum;
	--m_busyLinkNum;
	cout << "反还一个连接:[" << link << "], 当前空闲连接数:" << m_freeLinkNum << " 当前忙碌连接数:" << m_busyLinkNum << endl;
	pthread_rwlock_unlock(g_lock);
}

/*销毁连接池*/
void LinkPool::destroyLinkPool()
{
	pthread_rwlock_wrlock(g_lock);
	m_isDestory = true;
	while (m_linkList.size() > 0) {
		priDestroyLink();
	}
	pthread_rwlock_unlock(g_lock);
	//销毁忙碌未返还的连接
	while (m_busyLinkList.size() > 0) {
		destroyLink(m_busyLinkList.front());
	}
	//回收管理者线程
	pthread_join(m_adjust_tid, NULL);
	//销毁锁
	pthread_rwlock_destroy(g_lock);
	delete g_lock;
}

LinkPool::~LinkPool()
{
	destroyLinkPool();
}

/*创建多个连接*/
bool LinkPool::createLinks(int n)
{
	int i;
	if (m_freeLinkNum + m_busyLinkNum + 1 > m_maxLinkNum) {
		cout << "达到最大链接!" << endl;
		m_errno = ErrBusying;
		return false;
	}
	for (i = 0; i < n; ++i) {
		auto link = createLink();
		if (link) {
			m_linkList.push_back(link);
			++m_freeLinkNum;
			cout << "成功创造一个新连接:[" << link << "] 当前空闲连接数为:" << m_freeLinkNum << endl;
		}
		else {
			cout << "创建失败!" << endl;
			m_errno = ErrConnectFailed;
		}
	}
	if (NoErr == m_errno) {
		return true;
	}
	return false;
}

/*管理者执行函数:判断空闲*/
void * LinkPool::adjustFreeLinck(void * linkPool)
{
	LinkPool *pool = (LinkPool*)linkPool;
	int i;
	int checkLinkSecs = 0;
	while (!pool->m_isDestory) {
		sleep(pool->m_checkFreeLinkSecs);
		cout << "管理者开始检查!.." << endl;
		pthread_rwlock_rdlock(g_lock);
		int busyNum = pool->m_busyLinkNum;
		int freeNum = pool->m_freeLinkNum;
		pthread_rwlock_unlock(g_lock);
		//此处计算需要销毁的链接数, 不能销毁之后剩余的总连接不足最小连接数
		int destroyNum = freeNum - pool->m_minLinkNum + busyNum;
		if (destroyNum >= LINK_POOL_DEL_LINKS) {
			destroyNum = LINK_POOL_DEL_LINKS;
		}
		if (busyNum * 2 < freeNum && freeNum > pool->m_minLinkNum) {
			pthread_rwlock_wrlock(g_lock);
			cout << "管理者开始销空闲连接, 销毁连接数为:" << destroyNum << endl;
			for (i = 0; i < destroyNum; ++i) {
				pool->priDestroyLink();
			}
			pthread_rwlock_unlock(g_lock);
		}
		//检查连接池连接数是否小于最小连接数
		if (busyNum + freeNum < pool->m_minLinkNum) {
			pthread_rwlock_wrlock(g_lock);
			pool->createLinks(pool->m_minLinkNum - busyNum - freeNum);
			pthread_rwlock_unlock(g_lock);
		}
		//检查连接是否可用
		checkLinkSecs += pool->m_checkFreeLinkSecs;
		if (checkLinkSecs >= LINK_POOL_CHECK_KEEP_ALIVE_SECS) {
			checkLinkSecs = 0;
			pthread_rwlock_wrlock(g_lock);
			//遍历删除法
			for (list<void*>::iterator t = pool->m_linkList.begin(); t != pool->m_linkList.end();) {
				if (!pool->isUsed(*t)) {
					pool->priDestroyLink(*t);
					pool->m_linkList.erase(t++);
				}
				else {
					t++;
				}
			}
			pthread_rwlock_unlock(g_lock);
		}
	}
	return nullptr;
}

大家自行去除不需要的测试的内容, 这只是一个基类, 需要对应数据库的派生类, 关注本文的后续更新!

大家发现BUG的请留言, 这样好改进程序, 作为分享出来的回报吧

猜你喜欢

转载自blog.csdn.net/tonny7501/article/details/88776114
今日推荐