TinyWebSever源码逐行注释(六)_ sql_connection.cpp

前言

项目源码地址
项目详细介绍

项目简介:

Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.

  1. 使用 线程池 + 非阻塞socket + epoll(ET和LT均实现) + 事件处理(Reactor和模拟Proactor均实现) 的并发模型
  2. 使用状态机解析HTTP请求报文,支持解析GET和POST请求
  3. 访问服务器数据库实现web端用户注册、登录功能,可以请求服务器图片和视频文件
  4. 实现同步/异步日志系统,记录服务器运行状态
  5. 经Webbench压力测试可以实现上万的并发连接数据交换

sql_connection.cpp用于实现数据库的连接池。主要内容如下:

1.数据库连接池

  • 单例模式,保证唯一
  • list实现连接池
  • 连接池为静态大小
  • 互斥锁实现线程安全

2.校验

  • HTTP请求采用POST方式
  • 登录用户名和密码校验
  • 用户注册及多线程注册安全

原项目地址的注释较少不适合初学者,于是我将每行都加上了注释帮助大家更好的理解:

cpp 复制代码
#include <mysql/mysql.h> // 包含 MySQL 库,用于和 MySQL 数据库交互
#include <stdio.h>       // 标准输入输出库
#include <string>        // C++ 字符串库
#include <string.h>      // C 字符串处理库
#include <stdlib.h>      // 标准库,包含内存分配、进程控制等
#include <list>          // C++ STL 的 list 容器,用于存储 MySQL 连接
#include <pthread.h>     // 线程库,用于多线程同步
#include <iostream>      // C++ 标准输入输出流
#include "sql_connection_pool.h" // 连接池头文件

using namespace std; // 使用标准命名空间

// 构造函数,初始化成员变量
connection_pool::connection_pool()
{
	m_CurConn = 0;  // 当前已使用的连接数
	m_FreeConn = 0; // 当前空闲的连接数
}

// 获取连接池实例(单例模式)避免重复创建连接池对象,进而管理和复用 MySQL 连接。
connection_pool *connection_pool::GetInstance()
{
	static connection_pool connPool; // 静态实例,保证全局唯一
	return &connPool; // 返回连接池的指针
}

// 构造初始化函数,设置连接池的相关配置
void connection_pool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log)
{
	// 保存数据库连接的参数
	m_url = url;               // 数据库主机地址
	m_Port = Port;             // 端口号
	m_User = User;             // 数据库用户名
	m_PassWord = PassWord;     // 数据库密码
	m_DatabaseName = DBName;   // 数据库名
	m_close_log = close_log;   // 是否关闭日志

	// 创建最大连接数个 MySQL 连接并加入连接池
	for (int i = 0; i < MaxConn; i++)
	{
		MYSQL *con = NULL;
		con = mysql_init(con); // 初始化 MySQL 连接

		if (con == NULL) // 如果初始化失败,打印错误日志并退出程序
		{
			LOG_ERROR("MySQL Error");
			exit(1);
		}

		// 连接到 MySQL 数据库
		con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);

		if (con == NULL) // 如果连接失败,打印错误日志并退出程序
		{
			LOG_ERROR("MySQL Error");
			exit(1);
		}

		// 将成功连接的 MySQL 连接加入连接池列表
		connList.push_back(con);
		++m_FreeConn; // 增加空闲连接数
	}

	// 初始化信号量,用于控制连接的获取和释放
	reserve = sem(m_FreeConn); 

	m_MaxConn = m_FreeConn; // 设置最大连接数
}

// 从连接池中获取一个可用的 MySQL 连接
MYSQL *connection_pool::GetConnection()
{
	MYSQL *con = NULL;

	if (0 == connList.size()) // 如果连接池为空,返回 NULL
		return NULL;

	reserve.wait(); // 等待信号量,如果没有空闲连接则等待

	lock.lock(); // 加锁,防止多线程同时操作连接池

	// 从连接池中取出一个连接
	con = connList.front(); 
	connList.pop_front(); // 移除最前面的连接

	--m_FreeConn; // 空闲连接数减少
	++m_CurConn;  // 使用中的连接数增加

	lock.unlock(); // 解锁
	return con;    // 返回获取的连接
}

// 释放当前使用的 MySQL 连接,将其放回连接池
bool connection_pool::ReleaseConnection(MYSQL *con)
{
	if (NULL == con) // 如果连接为空,返回 false
		return false;

	lock.lock(); // 加锁,防止多线程同时操作连接池

	// 将连接放回连接池
	connList.push_back(con);
	++m_FreeConn; // 空闲连接数增加
	--m_CurConn;  // 使用中的连接数减少

	lock.unlock(); // 解锁

	reserve.post(); // 释放信号量,通知等待的线程有连接可用
	return true;
}

// 销毁连接池,关闭所有 MySQL 连接
void connection_pool::DestroyPool()
{
	lock.lock(); // 加锁,防止其他线程操作连接池
	if (connList.size() > 0)
	{
		// 遍历连接池中的所有连接,逐一关闭
		list<MYSQL *>::iterator it;
		for (it = connList.begin(); it != connList.end(); ++it)
		{
			MYSQL *con = *it;
			mysql_close(con); // 关闭 MySQL 连接
		}
		m_CurConn = 0; // 重置已使用连接数
		m_FreeConn = 0; // 重置空闲连接数
		connList.clear(); // 清空连接池列表
	}

	lock.unlock(); // 解锁
}

// 返回当前空闲的连接数
int connection_pool::GetFreeConn()
{
	return this->m_FreeConn; // 返回空闲连接数
}

// 析构函数,销毁连接池,释放所有连接
connection_pool::~connection_pool()
{
	DestroyPool(); // 调用销毁连接池的函数
}

// RAII 机制,自动管理 MySQL 连接的获取和释放
connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool){
	*SQL = connPool->GetConnection(); // 获取一个数据库连接并赋值给传入的指针

	conRAII = *SQL; // 保存获取到的连接
	poolRAII = connPool; // 保存连接池的引用
}

// 析构函数,自动释放连接回连接池
connectionRAII::~connectionRAII(){
	poolRAII->ReleaseConnection(conRAII); // 将连接放回连接池
}
相关推荐
鬼手点金33 分钟前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw
贵州诚鑫致达科技有限公司1 小时前
Linux的yum源怎么重启?源不是服务
linux·运维·服务器
余槐i1 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
数据库小学妹1 小时前
PostgreSQL表膨胀怎么办?autovacuum清不动的根因与排查命令
数据库·postgresql·表膨胀·mvcc·数据库运维
mister_guo1 小时前
MySQL-1267错误:排序规则冲突的原因与解决方案
数据库·mysql
骇客野人2 小时前
在麒麟操作系统上 分别安装 jdk1.8, 达梦数据库,redis ,memcache 的操作实施方案和步骤
数据库·redis·memcached
遇印记2 小时前
传输层,网络层
运维·服务器·网络·windows·学习
Elastic 中国社区官方博客3 小时前
一条 ES|QL 查询,涵盖365天的审计日志:在热数据和可搜索快照上执行相同的搜索
大数据·运维·服务器·elasticsearch·搜索引擎·全文检索
北京中科新远科技3 小时前
高速光模块上线验收实战:DOM、FEC、BER与端口计数器怎么一起看
服务器·网络·php
美狐美颜SDK开放平台4 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk