连接池初始化
OVERVIEW
1.Connection类
封装Connection类,在该类内调用mysql提供的接口实现对数据库的增删改查,
Connection.h
#ifndef _CONNECTION_H
#define _CONNECTION_H
#include <string.h>
#include <iostream>
#include <mysql/mysql.h>
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);
private:
// 表示和 MYSQL Server的一条连接
MYSQL *_conn;
};
#endif
Connection.cpp
#include "Connection.h"
Connection::Connection()
{
_conn = mysql_init(nullptr);
}
Connection::~Connection()
{
if (_conn != nullptr) mysql_close(_conn);
}
bool Connection::connect(string ip, unsigned short port, string user, string password, string dbname)
{
MYSQL *p = mysql_real_connect(_conn, ip.c_str(), user.c_str(), password.c_str(), dbname.c_str(), port, nullptr, 0);
return p != nullptr;
}
// insert delete update
bool Connection::update(string sql)
{
if (mysql_query(_conn, sql.c_str())) {
LOG("update failed!~" + sql);
return false;
}
return mysql_use_result(_conn);
}
2.CommonConnectionPool类
-
需求分析:
在整个系统中数据库连接池对象出现一次即可,有数据库连接需求时,从同一个数据库连接池中获取
Connection
对象即可,连接池不需要创建多个,所以可以通过单例模式来设计数据库连接池类,但是服务器肯定是多线程的,肯定会有多个线程都来获取连接池的单例,所以还需要保证数据库连接池的线程安全问题,
-
CommonConnectionPool类设计:需要通过单例模式实现,并保证数据库连接池的线程安全问题(线程安全的单例),
-
功能实现:启动连接池构造函数,读取基本的配置文件、初始化连接池数量initSize
CommonConnectionPool.h
#ifndef _COMMONCONNECTIONPOOL_H
#define _COMMONCONNECTIONPOOL_H
#include <queue>
#include <string>
#include <mutex>
#include <atomic>
#include <thread>
#include "Connection.h"
using namespace std;
// 实现连接池功能模块
class ConnectionPool {
public:
static ConnectionPool* getConnectionPool();
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; // 创建连接的数量 ++操作是线程安全的
};
#endif
CommonConnectionPool.cpp
#include "public.h"
#include "CommonConnectionPool.h"
// 懒汉单例模式
// 对于静态局部变量的初始化 由编译器自动进行 lock与unlock
ConnectionPool* ConnectionPool::getConnectionPool()
{
static ConnectionPool pool;
return &pool;
}
// 初始化连接池
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.启动一个新的线程作为连接的生产者
}
// 读取配置文件
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;
}