• C++11实现的数据库连接池


    它什么是?

    在这里插入图片描述
    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;类似的还有线程池。

    为什么要用?

    在这里插入图片描述
    一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。各种池化技术的使用原因都是类似的,也就是单独操作比较浪费系统资源,利用池提前准备一些资源,在需要时可以重复使用这些预先准备的资源,从而减少系统开销,实现资源重复利用。

    有什么区别?

    下面以访问MySQL为例,执行一个SQL命令,如果不使用连接池,需要经过哪些流程:

    • 建立通信连接的 TCP 三次握手
    • MySQL认证的三次握手
    • 真正的SQL执行
    • MySQL的关闭
    • 断开通信连接的 TCP 四次挥手

    如果使用了连接池第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。

    Github

    Connection.h

    //
    // Created by Cmf on 2022/8/24.
    //
    
    #ifndef CLUSTERCHATSERVER_CONNECTION_H
    #define CLUSTERCHATSERVER_CONNECTION_H
    
    #include 
    #include 
    #include 
    #include "Log.hpp"
    
    class Connection {
    public:
        Connection();
    
        ~Connection();
    
        bool Connect(const std::string &ip, const uint16_t port, const std::string &user, const std::string &pwd,
                     const std::string &db);
    
        bool Update(const std::string &sql);
    
        MYSQL_RES *Query(const std::string &sql);
    
        void RefreshAliveTime();
    
        long long GetAliveTime() const;
    
    private:
        MYSQL *_conn;
        std::chrono::time_point _aliveTime;
    };
    
    #endif //CLUSTERCHATSERVER_CONNECTION_H
    

    Connection.cpp

    //
    // Created by Cmf on 2022/8/24.
    //
    
    #include "Connection.h"
    
    Connection::Connection() {
        _conn = mysql_init(nullptr);
        mysql_set_character_set(_conn, "utf8");//设置编码格式维utf8
    }
    
    Connection::~Connection() {
        if (_conn != nullptr) {
            mysql_close(_conn);
        }
    }
    
    bool Connection::Connect(const std::string &ip, const uint16_t port, const std::string &user, const std::string &pwd,
                             const std::string &db) {
        _conn = mysql_real_connect(_conn, ip.c_str(), user.c_str(), pwd.c_str(), db.c_str(), port, nullptr, 0);
        if (_conn == nullptr) {
            LOG_ERROR("MySQL Connect Error")
            return false;
        }
        return true;
    }
    
    bool Connection::Update(const std::string &sql) {
        if (mysql_query(_conn, sql.c_str()) != 0) {
            LOG_INFO("SQL %s 更新失败:%d", sql.c_str(), mysql_error(_conn));
            return false;
        }
        return true;
    }
    
    MYSQL_RES *Connection::Query(const std::string &sql) {
        if (mysql_query(_conn, sql.c_str()) != 0) {
            LOG_INFO("SQL %s 查询失败:%d", sql.c_str(), mysql_error(_conn));
            return nullptr;
        }
        return mysql_use_result(_conn);
    }
    
    void Connection::RefreshAliveTime() {
        _aliveTime = std::chrono::steady_clock::now();
    }
    
    long long Connection::GetAliveTime() const {
        return std::chrono::duration_cast(std::chrono::steady_clock::now() - _aliveTime).count();
    }
    
    

    ConnectionPool.h

    //
    // Created by Cmf on 2022/8/24.
    //
    
    #ifndef CLUSTERCHATSERVER_COMMOMCONNECTIONPOOL_H
    #define CLUSTERCHATSERVER_COMMOMCONNECTIONPOOL_H
    
    #include "noncopyable.hpp"
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include "Connection.h"
    
    class ConnectionPool : private noncopyable {
    public:
        static ConnectionPool& GetConnectionPool(); //获取连接池对象实例
        //给外部提供接口,从连接池中获取一个可用的空闲连接
        std::shared_ptr GetConnection();//智能指针自动管理连接的释放
    
        ~ConnectionPool();
    
    private:
        ConnectionPool();
    
        bool LoadConfigFile();
    
        //运行在独立的线程中,专门负责生产新连接
        void ProduceConnectionTask();
    
        //扫描超过maxIdleTime时间的空闲连接,进行对于的连接回收
        void ScannerConnectionTask();
    
        void AddConnection();
    
    private:
        std::string _ip;
        uint16_t _port;
        std::string _user;
        std::string _pwd;
        std::string _db;
    
        size_t _minSize;  //初始链接数量
        size_t _maxSize;   //最大连接数量
        size_t _maxIdleTime;//最大空闲时间
        size_t _connectionTimeout;//超时时间
    
        std::queue _connectionQueue;//存储连接队列
        std::mutex _mtx;    //维护连接队列的线程安全互斥锁
        std::atomic_int _connectionCount;//记录连接所创建的connection连接的总数量
        std::condition_variable _cv;//设置条件变量,用于连接生产线程和连接消费线程的通信
    };
    
    #endif //CLUSTERCHATSERVER_COMMOMCONNECTIONPOOL_H
    

    ConnectionPool.cpp

    //
    // Created by Cmf on 2022/8/24.
    //
    #include 
    #include "ConnectionPool.h"
    #include "json.hpp"
    
    using json = nlohmann::json;
    
    ConnectionPool &ConnectionPool::GetConnectionPool() {
        static ConnectionPool pool;
        return pool;
    }
    
    std::shared_ptr ConnectionPool::GetConnection() {
        std::unique_lock lock(_mtx);
        while (_connectionQueue.empty()) {  //连接为空,就阻塞等待_connectionTimeout时间,如果时间过了,还没唤醒
            if (std::cv_status::timeout == _cv.wait_for(lock, std::chrono::microseconds(_connectionTimeout))) {
                if (_connectionQueue.empty()) { //就可能还是为空
                    continue;
                }
            }
        }
        //对于使用完成的连接,不能直接销毁该连接,而是需要将该连接归还给连接池的队列,供之后的其他消费者使用,于是我们使用智能指针,自定义其析构函数,完成放回的操作:
        std::shared_ptr res(_connectionQueue.front(), [&](Connection *conn) {
            std::unique_lock locker(_mtx);
            conn->RefreshAliveTime();
            _connectionQueue.push(conn);
        });
        _connectionQueue.pop();
        _cv.notify_all();
        return res;
    }
    
    ConnectionPool::ConnectionPool() {
        if (!LoadConfigFile()) {
            LOG_ERROR("JSON Config Error");
            return;
        }
        //创建初始数量的连接
        for (int i = 0; i < _minSize; ++i) {
            AddConnection();
        }
        //启动一个新的线程,作为连接的生产者 linux thread => pthread_create
        std::thread produce(std::bind(&ConnectionPool::ProduceConnectionTask, this));
        produce.detach();//守护线程,主线程结束了,这个线程就结束了
        //启动一个新的定时线程,扫描超过maxIdleTime时间的空闲连接,进行对于的连接回收
        std::thread scanner(std::bind(&ConnectionPool::ScannerConnectionTask, this));
        scanner.detach();
    }
    
    ConnectionPool::~ConnectionPool() {
        while (!_connectionQueue.empty()) {
            Connection *ptr = _connectionQueue.front();
            _connectionQueue.pop();
            delete ptr;
        }
    }
    
    bool ConnectionPool::LoadConfigFile() {
        std::ifstream ifs("../../config/dbconf.json");
        json js;
        ifs >> js;
        std::cout << js << std::endl;
        if (!js.is_object()) {
            LOG_ERROR("JSON is NOT Object");
            return false;
        }
        if (!js["ip"].is_string() ||
            !js["port"].is_number() ||
            !js["user"].is_string() ||
            !js["pwd"].is_string() ||
            !js["db"].is_string() ||
            !js["minSize"].is_number() ||
            !js["maxSize"].is_number() ||
            !js["maxIdleTime"].is_number() ||
            !js["timeout"].is_number()) {
            LOG_ERROR("JSON The data type does not match");
            return false;
        }
        _ip = js["ip"].get();
        _port = js["port"].get<uint16_t>();
        _user = js["user"].get();
        _pwd = js["pwd"].get();
        _db = js["db"].get();
        _minSize = js["minSize"].get<size_t>();
        _maxSize = js["maxSize"].get<size_t>();
        _maxIdleTime = js["maxIdleTime"].get<size_t>();
        _connectionTimeout = js["timeout"].get<size_t>();
        return true;
    }
    
    void ConnectionPool::ProduceConnectionTask() {
        while (true) {
            std::unique_lock lock(_mtx);
            while (_connectionQueue.size() >= _minSize) {
                _cv.wait(lock);
            }
            if (_connectionCount < _maxSize) {
                AddConnection();
            }
            _cv.notify_all();
        }
    }
    
    void ConnectionPool::ScannerConnectionTask() {
        while (true) {
            std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime));
            std::unique_lock lock(_mtx);
            while (_connectionCount > _minSize) {
                Connection *ptr = _connectionQueue.front();//队头的时间没超过,那后面的时间就都没超过
                if (ptr->GetAliveTime() >= _maxIdleTime * 1000) {
                    _connectionQueue.pop();
                    --_connectionCount;
                    delete ptr;
                } else {
                    break;
                }
            }
        }
    }
    
    void ConnectionPool::AddConnection() {
        Connection *conn = new Connection();
        conn->Connect(_ip, _port, _user, _pwd, _db);
        conn->RefreshAliveTime();
        _connectionQueue.push(conn);
        ++_connectionCount;
    }
    

    __EOF__

  • 本文作者: 放飞梦想
  • 本文链接: https://www.cnblogs.com/chengmf/p/16624309.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    随笔
    java计算机毕业设计医院出入院管理系统源码+系统+mysql数据库+lw文档
    中国锦纶行业市场全景调研及投资价值评估研究报告
    c++ trivial, standard layout和POD类型解析
    【LeetCode每日一题】——448.找到所有数组中消失的数字
    在 VMware vSphere 中构建 Kubernetes 存储环境
    Flask狼书笔记 | 09_图片社交网站 - 大型项目的架构与需求
    CentOs7.5 连接不上Xfp,XShell,防火墙未关闭
    asp.net网站的建立及运行
    千古第一文人苏轼的众CP
  • 原文地址:https://www.cnblogs.com/chengmf/p/16624309.html