【数据库连接池】04:连接池空闲连接回收

连接池空闲连接回收


OVERVIEW

  • 连接池空闲连接回收
      • 1.需求分析
      • 2.Connection添加字段
      • 3.CommonConnectionPool中调用刷新
      • 4.CommonConnectionPool中定时扫描
        • CommonConnectionPool.h
        • CommonConnectionPool.cpp

1.需求分析

多余的连接如果超过指定的时间,还没有再次被使用到,就需要将连接进行回收,从而节省服务器资源,

实现方案:

  1. 在Connection类中添加_alivetime字段,用于记录连接进入空闲状态后的起始时间,
  2. 每次入队或者出队时都将Connection连接对象的_alivetime字段进行刷新,
  3. 启动一个定时线程,定时扫描超过maxIdleTime的空闲连接,进行多余连接的回收操作

2.Connection添加字段

#ifndef _CONNECTION_H
#define _CONNECTION_H

#include 
#include 
#include 
#include 
using namespace std;

#define LOG(str)									\
	cout << __FILE__ << ":" << __LINE__ << " " <<	\
	__TIMESTAMP__ << ":" << str << endl;

// 封装Connection对象 实现数据库增删改查
class Connection {
public:
	// 初始化数据库连接
	Connection();
    // 释放数据库连接资源
    ~Connection();
    // 连接数据库
    bool connect(string ip, unsigned short port, string user, string password, string dbname);
    // 更新数据库操作 insert delete update
    bool update(string sql);
    // 刷新连接的起始空闲时间点
    void refreshAliveTime() { _alivetime = clock(); }
    // 返回连接存活的时长
    clock_t getAliveTime() const { return clock() - _alivetime; }
private:
    // 表示和 MYSQL Server的一条连接
    MYSQL *_conn;
    // 进入空闲状态后的起始时间
    clock_t _alivetime;
};

#endif

3.CommonConnectionPool中调用刷新

  1. 在初始化连接池时,添加刷新Connection连接的alivetime

    // 初始化连接池
    ConnectionPool::ConnectionPool() {
    	// 1.加载配置项
    	if (!loadConfigFile()) return;
    	// 2.创建初始数量的连接
    	for (int i = 0; i < _initSize; ++i) {
    		Connection *p = new Connection();
    		p->connect(_ip, _port, _username, _password, _dbname);
    		p->refreshAliveTime();
    		_connectionq.push(p);
    		_connectionCnt++;
    	}
    	// 3.启动一个新的线程作为连接的生产者
    	thread connProducer(std::bind(&ConnectionPool::produceConnTask, this));
    }
    
  2. 在消费者生产Connection连接时,刷新连接的alivetime

    // 生产者线程的线程函数
    void ConnectionPool::produceConnTask()
    {
    	for (;;) {
    		// 队列上锁
    		unique_lock<mutex> lock(_queueMutex);
    		// 若队列不空 生产者线程进入等待状态 并且将锁释放 等待为空
    		while (!_connectionq.empty()) {
    			condv.wait(lock);
    		}
    		// 连接数量没有到达上限 则继续创建新的连接
    		if (_connectionCnt < _maxSize) {
    			Connection *p = new Connection();
    			p->connect(_ip, _port, _username, _password, _dbname);
    			p->refreshAliveTime();
    			_connectionq.push(p);
    			_connectionCnt++;
    		}
    		// 通知消费者线程可以消费连接了
    		condv.notify_all();
    	}
    }
    
  3. 在消费者使用完毕,归还数据库连接时,需要刷新连接的alivetime

    // 返回连接对象 使用智能指针管理
    shared_ptr<Connection> sp(_connectionq.front(), [&](Connection *pcon){
        unique_lock<mutex> lock(_queueMutex);
        pcon->refreshAliveTime();
        _connectionq.push(pcon);
    });
    

4.CommonConnectionPool中定时扫描

启动一个定时线程,定时扫描超过maxIdleTime的空闲连接,进行多余连接的回收操作

CommonConnectionPool.h
#ifndef _COMMONCONNECTIONPOOL_H
#define _COMMONCONNECTIONPOOL_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Connection.h"
using namespace std;

// 实现连接池功能模块
class ConnectionPool {
public:
	// 获取连接池实例
	static ConnectionPool* getConnectionPool();
	// 获取数据库连接
	shared_ptr<Connection> getConnection();
private:
	ConnectionPool();
	// 加载外部配置文件
	bool loadConfigFile();
	// 生产新连接任务函数
	void produceConnTask();
	// 定时扫描空闲连接 并进行回收操作
	void collectConnTask();
private:
	// 连接池参数
	int _initSize;				// 初始连接量
	int _maxSize;				// 最大连接量
	int _maxIdleTime;			// 最大空闲时间
	int _connectionTimeout;		// 连接超时时间
	// 数据库信息
	string _ip;
	unsigned short _port;
	string _username;
	string _password;
	string _dbname;
	// 数据库连接存储队列
	queue<Connection*> _connectionq;		// 存储mysql连接的队列
	mutex _queueMutex;						// 维护连接队列的线程安全的互斥锁
	atomic_int _connectionCnt;				// 创建连接的数量 ++操作是线程安全的
	condition_variable condv;					// 条件变量 用于连接生产线程和连接消费线程间的通信
};

#endif
CommonConnectionPool.cpp
// 初始化连接池
ConnectionPool::ConnectionPool() {
	// 1.加载配置项
	if (!loadConfigFile()) return;
	// 2.创建初始数量的连接
	for (int i = 0; i < _initSize; ++i) {
		Connection *p = new Connection();
		p->connect(_ip, _port, _username, _password, _dbname);
		p->refreshAliveTime();
		_connectionq.push(p);
		_connectionCnt++;
	}
	// 3.启动一个新的线程作为连接的生产者
	thread connProducer(std::bind(&ConnectionPool::produceConnTask, this));
	// 设置为守护线程
	connProducer.detach();
	// 4.启动一个新的线程 定时扫描超过maxIdleTime时间的空闲连接 对空闲连接进行回收操作
	thread connCollector(std::bind(&ConnectionPool::collectConnTask, this));
	// 设置为守护线程
	connCollector.detach();
}

// 定时扫描空闲连接 并进行回收操作
void ConnectionPool::collectConnTask()
{
	for (;;) {
		// 通过调用sleep模拟定时效果
		this_thread::sleep_for(chrono::seconds(_maxIdleTime));
		// 扫描整个连接池队列 释放多余连接
		unique_lock<mutex> lock(_queueMutex);
		while (_connectionCnt > _initSize) {
			Connection *p = _connectionq.front();
			if (p->getAliveTime() < (_maxIdleTime * 1000)) break;
			_connectionq.pop();
			_connectionCnt--;
			delete p;//调用Connection的析构函数释放连接
		}
	}
}

你可能感兴趣的:(teamtalk源码解读,数据库)