Combate de C++: grupo de conexiones de mysql basado en las nuevas funciones de c++11

Instalar MySQL

sudo apt-get install mysql-server //servidor mysql

sudo apt-get install mysql-client //cliente mysql -- fácil de operar

sudo apt-get install libmysqlclient-dev //biblioteca desarrollada mysql

Descripción general del grupo de conexiones de base de datos

1. Si no se utiliza el grupo de conexiones, es necesario crear y destruir subprocesos cada vez que el servidor interactúa con la base de datos. Una gran cantidad de creación y destrucción de subprocesos consume recursos del sistema

2. La base de datos mysql se comunica con el servidor a través de TCP. De acuerdo con las reglas de comunicación de TCP, es necesario establecer y desconectar la conexión. Este proceso es relativamente lento y engorroso.

3. Se requiere autenticación para cada conexión

Al crear previamente una cierta cantidad de conexiones, colóquelas en un grupo. Cuando el cliente tiene una solicitud, el servidor necesita interactuar con mysql, por lo que solo necesita sacar una conexión del grupo y colocar la conexión en el grupo de conexiones cuando se completa la operación. Esto evita la creación y destrucción frecuente de subprocesos.

Solo un grupo es suficiente y hay muchas conexiones en un grupo. Por lo tanto, es más apropiado usar el modo singleton para la clase de grupo de subprocesos.

 Pasos para crear un grupo de subprocesos

1. En primer lugar, debemos tener una conexión, es decir, debemos preparar la conexión en la piscina con anticipación. ¿Cómo lograr estas conexiones?

Muy simple, encapsula la API provista por libmysqlclient-dev. De acuerdo con los requisitos generales, las funciones que debe tener una conexión: conexión, actualización, consulta, procesamiento de transacciones, estado de la conexión (tiempo de inactividad)

Optimización: Cuando la base de datos se usa por un período de tiempo, la cantidad de conexiones tiende a ser estable, por lo que el grupo de conexiones que diseñamos tiene la función de administrar automáticamente la cantidad de conexiones.

2. Diseñe un grupo para almacenar y administrar estas conexiones

Usa la cola:

        1) Las características de la cola --> primero en entrar, primero en salir

        2) También podríamos insertar cada conexión actualizada al final de la cola. Luego, la conexión al principio de la cola debe estar inactiva durante mucho tiempo. Si se excede el tiempo máximo de inactividad, destruiremos el hilo.

        3) También puede usar el montón superior pequeño, pero no es necesario y es muy problemático de implementar

 Puntos técnicos involucrados

1. Programación de subprocesos múltiples

2. Sincronización de subprocesos (mutex, variable de condición)

3. biblioteca crono (tiempo de procesamiento)

4. Puntero inteligente

5. expresión lambda

6. Modo único

7. Encapsule la operación de datos API de MySQL

8. Contenedor STL

9. Modelo de consumidor productor

10. Biblioteca Jsoncpp

detalles técnicos

La encapsulación de API ===> puede entenderse como una conexión en el grupo de conexiones

1. Inicializar, obtener un objeto para operar la instancia de la base de datos (programación orientada a objetos)

   MYSm_conn = mysql_init(nullptr); //devuelve un objeto MYSQL

   mysql_set_character_set(m_conn,"utf8"); //Establecer codificación

2. Conéctese a la base de datos especificada: nombre de la base de datos, IP, nombre de usuario, contraseña y otra información ===> parámetros proporcionados por el usuario

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *usuario, const char *passwd, const char *db, puerto int sin firmar, const char *unix_socket, bandera de cliente larga sin firmar)

3. Operaciones relacionadas con la base de datos

          ACTUALIZAR: agregar, eliminar, insertar int mysql_query(MYSQL *mysql, const char *q)

          Consulta: ejecutar la instrucción mysql int mysql_query(MYSQL *mysql, const char *q)

                     Obtener el conjunto de resultados MYSQL_RES *mysql_store_result(MYSQL *mysql)

                     Obtener el contenido de una fila MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)

   Procesamiento de transacciones: operación de transacciones, reversión de transacciones, confirmación de transacciones

          my_bool mysql_autocommit(MYSQL *mysql, my_bool auto_mode)

          my_bool mysql_commit(MYSQL *mysql)

          my_bool mysql_rollback(MYSQL *mysql)

Diseñe el grupo de conexiones ==> administre la conexión encapsulada arriba

1. Debido a que solo se necesita un grupo, se adopta el modo singleton

        ¿Qué es el patrón singleton? El patrón singleton es un patrón de creación que proporciona la mejor manera de crear objetos. Este patrón involucra una sola clase que es responsable de crear su propio objeto único. El objeto se puede obtener del mundo exterior, pero no se puede crear ni copiar.

        En términos sencillos: el patrón singleton significa que cada clase tiene un único objeto, y este objeto es creado por el propio objeto. Si permite que el exterior llame al constructor o copie el constructor, etc., es difícil asegurarse de que solo se cree un objeto de instancia en el exterior. Para el acceso externo a

2. Utilizar el modelo consumidor productor: tres elementos (productor, consumidor, contenedor)

        Debido a la competencia de recursos y subprocesos múltiples, definitivamente involucrará el problema de la sincronización de subprocesos.

        Resuelva el problema de sincronización de subprocesos:

        Solo use mutexes

        Use variables de condición + mutexes: el uso efectivo de variables de condición puede evitar una competencia sin sentido entre consumidores cuando la cola actual está vacía y una competencia sin sentido entre productores cuando la cola está llena.

3. Lea la información sobre cómo conectarse a la base de datos MySQL a través del archivo de configuración

        XXXconfig.json ==> Use la biblioteca jsoncpp para analizar el archivo json

        Pasos específicos:

                1) Instale la biblioteca de terceros Jsoncpp: sudo apt-install Jsoncpp-dev

                2) Debe usarse con <fstream>

                3) Leer el archivo de configuración

                        Definir el objeto Reader rd

                        Definir la raíz del objeto de valor

                        Almacene el contenido del archivo json señalado por ifs en la raíz, que es una matriz bidimensional

                        Determine si la construcción es exitosa: root.isobject()

                        root[key] obtiene el valor correspondiente, por ejemplo: m_username = root["username"];

4. Proporcionar una interfaz externa para obtener conexiones

        El externo necesita obtener una conexión del grupo de conexiones y operar la base de datos a través de esta conexión.

        Cómo diseñar la interfaz externa:

        Use un puntero inteligente como valor de retorno, ya que la dirección obtiene la conexión, pasar el puntero puede ahorrar tiempo

        Los punteros inteligentes pueden ayudarnos a liberar conexiones automáticamente. Esta conexión puede ser adquirida por varios subprocesos de trabajo. El uso de shared_ptr mantendrá un recuento de referencia. Cuando el recuento de referencia es 0, el espacio de memoria señalado por el puntero se liberará automáticamente. Evite que los usuarios se olviden de liberar memoria, lo que provocaría fugas de memoria.

        Implementación interna de la función: De hecho, es el proceso de los consumidores tomando productos de la cola.

        La nueva característica de c ++ 11, usa la plantilla unique_lock para envolver mutex, bloquear y desbloquear automáticamente. Bloqueado en la creación y desbloqueado en la destrucción. Generalmente, es una variable local, por lo que se destruirá con el tiempo.

        La nueva función de c ++ 11, use wait_for para esperar un cierto período de tiempo, si la cola aún está vacía después de esperar un cierto período de tiempo, ya no bloqueará la espera y juzgará si la cola está vacía de nuevo (método de sondeo)

        Use una lambda para devolver la conexión adquirida y actualice el punto de inicio del tiempo de inactividad de la conexión dentro del cuerpo de la función

5. El conjunto de conexiones realiza una gestión automática de la cantidad de conexiones (crea una conexión cuando no es suficiente y destruye la conexión con un tiempo de inactividad prolongado)

        A través de dos subprocesos, debido a que el subproceso principal no se puede bloquear en este lugar, se completa a través de dos subprocesos y se establece la separación del subproceso

        Cree conexiones automáticamente cuando no sea suficiente:

                El grupo de subprocesos mantiene dos datos, un número mínimo de conexiones y un número máximo de conexiones

         Destruya las conexiones que han estado inactivas durante mucho tiempo:

                Tome la conexión del principio de la cola y compare el tiempo de inactividad de la conexión con el tiempo de inactividad máximo establecido. Si es mayor o igual, se eliminará de la cola y se destruirá.

el código

#pragma one
#include <mysql/mysql.h>
#include <string>
#include <chrono>

using namespace std::chrono_literals;
using namespace std;
using namespace chrono;

class MysqlConn
{
public:
    //创建一个MYSQL实例对象并设置字符集
    MysqlConn();
    //是放在资源
    ~MysqlConn();
    //连接指定的数据库
    bool connect(string ip,string user,string passwd,string bdName,unsigned int port);
    //更新:增加、删除、修改
    bool undate(string sql);
    //查询
    bool query(string sql);
    //遍历得到的结果集
    bool next();
    //获取结果集里的值
    string value(int index);
    //事务处理提交方式
    bool transaction();
    //事务提交
    bool commit();
    //事务回滚
    bool rollback();
    //更新空闲时间点
    void refreshAliveTime();
    //计算连接空闲时长
    long long getAliveTime();
private:
    //每次搜索都需要更新结果集
    void freeResult();
    MYSQL *m_conn = nullptr;
    MYSQL_RES *m_result = nullptr;
    MYSQL_ROW m_row;

    steady_clock::time_point m_aliveTime;

};
#include "MysqlConn.h"


MysqlConn::MysqlConn()
{
    //获取一个MYSQL句柄
    m_conn = mysql_init(nullptr);
    //设置字符集
    mysql_set_character_set(m_conn,"utf8");
}

MysqlConn:: ~MysqlConn()
{
    if(m_conn != nullptr)
    {
        mysql_close(m_conn);
    }
    freeResult();
}

bool MysqlConn::connect(string ip,string user,string passwd,string dbName,unsigned int port)
{
    /*
    MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, 
    const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag)
    */
    MYSQL *p = mysql_real_connect(m_conn,ip.c_str(),user.c_str(),passwd.c_str(),dbName.c_str(),port,nullptr,0);
    return p != nullptr;
}

bool MysqlConn::undate(string sql)
{
    if(mysql_query(m_conn,sql.c_str()))
    {
        return false;
    }

    return true;
}

bool MysqlConn::query(string sql)
{
    freeResult();
    if(mysql_query(m_conn,sql.c_str()))
    {
        return false;
    }
    //得到结果集
    m_result = mysql_store_result(m_conn);

    return true;
}

bool MysqlConn::next()
{
    if(m_result != nullptr)
    {
        m_row = mysql_fetch_row(m_result);  //获取一行
        if(m_row != nullptr)
        {
            return true;
        }
    }

    return false;
}

string MysqlConn::value(int index)
{

    int rowCount = mysql_num_fields(m_result);  //返回结果集中字段数目
    if(index >= rowCount || index < 0)
    {
        return string();
    }

    char* ans = m_row[index];
    unsigned long length = mysql_fetch_lengths(m_result)[index];

    return string(ans,length);

}

bool MysqlConn::transaction()
{
    return mysql_autocommit(m_conn,false);     //自动提交改为自动提交
}

bool MysqlConn::commit()
{
    return mysql_commit(m_conn);
}

bool MysqlConn::rollback()
{
    return mysql_rollback(m_conn);
}

void MysqlConn::freeResult()
{
    if(m_result)
    {
        mysql_free_result(m_result);
        m_result = nullptr;
    }
}

void MysqlConn::refreshAliveTime()
{
   m_aliveTime = steady_clock::now();
}

//计算连接空闲时长
long long MysqlConn::getAliveTime()
{
    nanoseconds  res = steady_clock::now() - m_aliveTime;       //nanosecods 纳秒
    milliseconds mil = duration_cast<microseconds>(res);        //将纳秒转成微妙

    return mil.count();
}

#include "MysqlConn.h"
#include <mutex>
#include <condition_variable>
#include <queue>

class ConnectionPool
{
public:
    //对外接口,获取线程池
    static ConnectionPool  *getConnectPool();    //静态局部变量是线程安全的
    //获取线程池中的连接
    shared_ptr<MysqlConn>   getConnection();
    //防止外界通过拷贝构造函数和移动拷贝构造函数
    ConnectionPool(const ConnectionPool &obj) = delete;
    ConnectionPool& operator=(const ConnectionPool& obj) = delete;
    ~ConnectionPool();
private:
    //构造函数私有化
    ConnectionPool();
    //解析配置文件
    bool parseJsonFile();

    //任务函数
    void produceConnection();   //生产数据库连接
    void recycleConnection();   //销毁数据库连接
    void addConnect();          //添加连接

private:
    string m_user;
    string m_passwd;
    string m_ip;
    string m_dbName;
    unsigned short m_port;
    //连接的上限和下限,自动维护线程池的连接数
    int m_minSize;
    int m_maxSize;
    //连接的超时时长
    int m_timeout;
    int m_maxIdleTime;
    //线程同步  
    mutex m_mutexQ;                     //互斥锁
    condition_variable m_cond;          //条件变量
    queue<MysqlConn *>m_connectionQ;    //共享资源

};
#include "ConnectionPool.h"
#include <fstream>
#include <thread>
#include <jsoncpp/json/json.h>

bool ConnectionPool::parseJsonFile()
{
    //获取配置文件
    fstream ifs("../dbconf.json");
    //读配置文件
    Json::Reader rd;
    Json::Value  root;
    rd.parse(ifs,root);     //解析配置文件
    if(root.isObject())
    {
        m_ip      = root["ip"].asString();
        m_user    = root["username"].asString();
        m_passwd  = root["password"].asString();
        m_dbName  = root["dbName"].asString();
        m_port    = root["port"].asInt();
        m_minSize = root["minSize"].asInt();
        m_maxSize = root["maxSize"].asInt();
        m_timeout = root["timeout"].asInt();
        m_maxIdleTime = root["maxTdleTime"].asInt();
        return true;
    }
    return false;
}

void ConnectionPool::addConnect()
{

    MysqlConn *conn = new MysqlConn;
    /*
        bool MysqlConn::connect(std::string ip, std::string user, std::string passwd,
        std::string bdName, unsigned int port)
    */
    conn->connect(m_ip,m_user,m_passwd,m_dbName,m_port);
    conn->refreshAliveTime();
    m_connectionQ.push(conn);
}

ConnectionPool::ConnectionPool()
{
    //加载配置文件
    if(!parseJsonFile())
    {
        return;
    }
    
    //创建最少连接数
    for(int i=0;i<m_minSize;++i)
    {
        addConnect();
    }

    //创建子线程用于检测并创建新的连接
    thread producer(&ConnectionPool::produceConnection,this);
    //销毁连接,检测并销毁连接
    thread recycler(&ConnectionPool::recycleConnection,this);

    //设置线程分离
    producer.detach();
    recycler.detach();
}

//子线程-->任务函数
void ConnectionPool::produceConnection()
{
    //通过轮询的方式不断的去检测
    while(true) 
    {
        //操作共享资源,需要加锁
        unique_lock<mutex> locker(m_mutexQ);
        //判断连接数是否达到容量,如果大于等于容量则需要阻塞一段时间
        while (m_connectionQ.size() >= m_maxSize)   
        {
           m_cond.wait(locker);
        }
        addConnect();
        m_cond.notify_all();        //唤醒消费者
    }
}
void ConnectionPool::recycleConnection()
{
    while(true)
    {
       //休眠一定的时长
       this_thread::sleep_for(chrono::milliseconds(500));
       unique_lock<mutex> locker(m_mutexQ);
       //让线程池中最少保持用于 m_minSize个线程
       while(m_connectionQ.size() > m_minSize)
       {
            MysqlConn *recyConn = m_connectionQ.front();
            //如果超时则销毁
            if(recyConn->getAliveTime() >= m_maxIdleTime)
            {
                m_connectionQ.pop();
                delete recyConn;
            } 
            else
            {
                break;
            }
       }

    }
}

ConnectionPool::~ConnectionPool()
{
    while(!m_connectionQ.empty())
    {
        MysqlConn *conn = m_connectionQ.front();
        m_connectionQ.pop();
        delete conn;
    }
}

//对外接口,获取线程池
ConnectionPool* ConnectionPool::getConnectPool()
{
    static ConnectionPool pool;
    return &pool;
}
//获取线程池中的连接
shared_ptr<MysqlConn> ConnectionPool::getConnection()
{
    //需要操作共享资源
    unique_lock<mutex> locker(m_mutexQ);
    //判断连接池队列为空
    while(m_connectionQ.empty())
    {
        if(cv_status::timeout == m_cond.wait_for(locker,chrono::milliseconds(m_timeout)))
        {
            if(m_connectionQ.empty())
            {
                continue;
            }
        }
    }
    //自定义shared_ptr析构方法,重新将连接放回到连接池中,而不是销毁
    shared_ptr<MysqlConn> connptr(m_connectionQ.front(),[this](MysqlConn *conn){
        unique_lock<mutex> locker(m_mutexQ);
        conn->refreshAliveTime();
        m_connectionQ.push(conn);   
    });
    //弹出,放到了队尾
    m_connectionQ.pop();
    m_cond.notify_all();
    return connptr;
}

 

Supongo que te gusta

Origin blog.csdn.net/weixin_46120107/article/details/126782449
Recomendado
Clasificación