【数据库连接池】03:连接池消费者操作

连接池消费者操作


1.需求分析

  • 外部获取数据库连接:

    ConnectionPool连接池需要向外部提供getConnection()接口,以便外部获取可用的空闲数据库连接,

    在外部使用完数据库连接后,可以不需要考虑再提供类似backConnection()操作来归还连接,而是可以使用智能指针(智能指针出作用域会自动析构、进而释放Connection)

    shared_ptr<Connection> getConnection();
    

    核心代码实现逻辑:

    // 获取数据库连接
    shared_ptr<Connection> ConnectionPool::getConnection()
    {
    	// 队列上锁
    	unique_lock<mutex> lock(_queueMutex);
    	// 如果队列为空 则将锁释放 等待最大超时时间
    	if (_connectionq.empty()) {
    		// 在指定的时间之内 如果有可获取的连接 则直接将连接取走
    		condv.wait_for(lock, chrono::milliseconds(_connectionTimeout));
    		// 线程被唤醒之后队列仍然为空 则获取连接失败
    		if (_connectionq.empty()) {
    			LOG("获取空闲连接超时...");
    			return nullptr;
    		}
    	}
    	// 返回连接对象 使用智能指针管理
    	shared_ptr<Connection> sp(_connectionq.front());
    	_connectionq.pop();
    	// 通知生产者线程检查队列是否为空
    	condv.notify_all();
        return sp;
    }
    
  • 还需要对智能指针重定义析构函数,在析构时不要将数据库连接释放掉,而是将连接归还到数据库连接池中,

    shared_ptr构造函数第2个参数自动接收一个删除器(函数对象),直接使用lambda表达式创建一个删除器,并将其传入构造函数中

    // 获取数据库连接
    shared_ptr<Connection> ConnectionPool::getConnection()
    {
    	// 队列上锁
    	unique_lock<mutex> lock(_queueMutex);
    	// 如果队列为空 则将锁释放 等待最大超时时间
    	if (_connectionq.empty()) {
    		// 在指定的时间之内 如果有可获取的连接 则直接将连接取走
    		condv.wait_for(lock, chrono::milliseconds(_connectionTimeout));
    		// 线程被唤醒之后队列仍然为空 则获取连接失败
    		if (_connectionq.empty()) {
    			LOG("获取空闲连接超时...");
    			return nullptr;
    		}
    	}
    	// 返回连接对象 使用智能指针管理
    	shared_ptr<Connection> sp(_connectionq.front(), [&](Connection *pcon){
    		unique_lock<mutex> lock(_queueMutex);
    		_connectionq.push(pcon);
    	});
    	_connectionq.pop();
    	// 通知生产者线程检查队列是否为空
    	condv.notify_all();
        return sp;
    }
    

2.CommonConnectionPool.h

#ifndef _COMMONCONNECTIONPOOL_H
#define _COMMONCONNECTIONPOOL_H

#include <queue>
#include <string>
#include <mutex>
#include <atomic>
#include <thread>
#include <memory>
#include <functional>
#include <condition_variable>
#include "Connection.h"
using namespace std;

// 实现连接池功能模块
class ConnectionPool {
public:
	// 获取连接池实例
	static ConnectionPool* getConnectionPool();
	// 生产新连接任务函数
	void produceConnTask();
	// 获取数据库连接
	shared_ptr<Connection> getConnection();
private:
	ConnectionPool();
	bool loadConfigFile();
	// 连接池参数
	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

3.CommonConnectionPool.cpp

#include "public.h"
#include "CommonConnectionPool.h"

// 懒汉单例模式
// 对于静态局部变量的初始化 由编译器自动进行 lock与unlock
ConnectionPool* ConnectionPool::getConnectionPool()
{
	static ConnectionPool pool;
    return &pool;
}

// 生产者线程的线程函数
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);
			_connectionq.push(p);
			_connectionCnt++;
		}
		// 通知消费者线程可以消费连接了
		condv.notify_all();
	}
}

// 获取数据库连接
shared_ptr<Connection> ConnectionPool::getConnection()
{
	// 队列上锁
	unique_lock<mutex> lock(_queueMutex);
	// 如果队列为空 则将锁释放 等待最大超时时间
	if (_connectionq.empty()) {
		// 在指定的时间之内 如果有可获取的连接 则直接将连接取走
		condv.wait_for(lock, chrono::milliseconds(_connectionTimeout));
		// 线程被唤醒之后队列仍然为空 则获取连接失败
		if (_connectionq.empty()) {
			LOG("获取空闲连接超时...");
			return nullptr;
		}
	}
	// 返回连接对象 使用智能指针管理
	shared_ptr<Connection> sp(_connectionq.front(), [&](Connection *pcon){
		unique_lock<mutex> lock(_queueMutex);
		_connectionq.push(pcon);
	});
	_connectionq.pop();
	// 通知生产者线程检查队列是否为空
	condv.notify_all();
    return sp;
}

// 初始化连接池
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);
		_connectionq.push(p);
		_connectionCnt++;
	}
	// 3.启动一个新的线程作为连接的生产者
	thread connProducer(std::bind(&ConnectionPool::produceConnTask, this));
}

// 读取配置文件
bool ConnectionPool::loadConfigFile()
{
	FILE *fp = fopen("config.conf", "r");
	if (fp == nullptr) {
		LOG("Config file error, file not exists!");
		return false;
	}
	while (!feof(fp)) {
		// 逐行读取配置文件
		char line[1024] = { 0 };
		fgets(line, 1024, fp);
		string str = line;
		// 查找配置项
		int idx = str.find('=', 0);
		if (idx == -1) continue;// 无效配置项
		int edx = str.find('\n', 0);
		string key = str.substr(0, idx);
		string value = str.substr(idx + 1, edx - idx - 1);
		// 初始化成员变量
		if (key == "ip") {
			_ip = value;
		} else if (key == "port") {
			_port = atoi(value.c_str());
		} else if (key == "username") {
			_username = value;
		} else if (key == "password") {
			_password = value;
		} else if (key == "dbname") {
			_dbname = value;
		} else if (key == "initSize") {
			_initSize = atoi(value.c_str());
		} else if (key == "maxSize") {
			_maxSize = atoi(value.c_str());
		} else if (key == "maxIdleTime") {
			_maxIdleTime = atoi(value.c_str());
		} else if (key == "connectionTimeout") {
			_connectionTimeout = atoi(value.c_str());
		}
	}
	return true;
}
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值