文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

QT实现检测MySQL数据库连接状态并断开重连

2023-08-20 12:18

关注

背景

在QT中做有关于MySQL的业务时,有时候需要知道MySQL数据库的当前连接状态。但QT在提供的QSqlDatabase模块中并没有相关的功能,不像QTcpSocket在连接服务或断开服务时都有相应的信号发出。

网上方法

经测试,在突然与数据库所在网络断开后,该接口仍返回true,不靠谱。

未测试,看网上的说法是在长时间未操作(默认8小时)数据库则会与数据库断开,如设置这个参数则会重连,但不知道在断网后的情况怎样。

使用定时器,定时执行sql语句(“select 1”)来判断数据库是否连接正常。经测试,在突然断网的情况下,执行sql语句会阻塞20秒甚至更长的时间(不知道这个时间是由什么决定的?可否设置?);但若是数据库服务挂掉,则不会阻塞。

解决方法

采用第3种方法 + 每次执行sql前ping一下网络是否连接正常,以此来避免断网时执行sql的阻塞。
(1)子线程中创建专门的连接来检测数据库连接状态;
(2)其他线程则创建自己的连接来执行相应的业务;并可根据子线程检测的状态结果来做相应的判断。
如果有别的方法,还请大佬们指导一下

代码

.h文件

#ifndef DBHEARTBEATTHREAD_H#define DBHEARTBEATTHREAD_H#include #include #include class DBHeartbeatThread : public QThread{    Q_OBJECTpublic:    enum DBType : quint8 {        MySQL = 0,        SqlServer,        Oracle    };    struct ConnectParam {        QString ip;        quint16 port;        QString dbName;        QString userName;        QString password;    };public:    explicit DBHeartbeatThread(QObject *parent = nullptr);    explicit DBHeartbeatThread(const DBType &type, const ConnectParam &param, QObject *parent = nullptr);public:    void setDBTypeAndConnectParm(const DBType &type, const ConnectParam &param);    void setCheckIpIsOnlineInterval(int msec);    void setConnectDBInterval(int msec);    void setCheckDBHeartbeatInterval(int msec);    QString lastError() const;protected:    void run() override;signals:    void signal_connectedDB();       // 数据库连接成功    void signal_disconnectedDB();    // 与数据库断开连接    void signal_connectedNet();      // 网络连接成功    void signal_disconnectedNet();   // 网络断开连接private:    DBType m_type;    ConnectParam m_param;    int m_checkIpIsOnlineInterval;    int m_connectDBInterval;    int m_checkDBHeartbeatInterval;    QMap<DBType, QString> m_mapDBTypeDriver;    QString m_lastError;    const int kCheckIpFailTime = 3;    // 检测网络心跳失败最大次数    const int kCheckDBFailTime = 3;    // 检测数据库心跳失败最大次数};#endif // DBHEARTBEATTHREAD_H

.cpp文件

#include "dbheartbeatthread.h"#include #include #include #include #include #include #include DBHeartbeatThread::DBHeartbeatThread(QObject *parent) : QThread(parent){    m_checkIpIsOnlineInterval = 100;    m_connectDBInterval = 100;    m_checkDBHeartbeatInterval = 100;    m_lastError = "";    m_mapDBTypeDriver = {{MySQL, "QMYSQL"}, {SqlServer, "QODBC"}, {Oracle, "QOCI"}};}DBHeartbeatThread::DBHeartbeatThread(const DBType &type, const ConnectParam &param, QObject *parent)    : DBHeartbeatThread(parent){    m_type = type;    m_param = param;}void DBHeartbeatThread::setDBTypeAndConnectParm(const DBHeartbeatThread::DBType &type,        const DBHeartbeatThread::ConnectParam &param){    m_type = type;    m_param = param;}void DBHeartbeatThread::setCheckIpIsOnlineInterval(int msec){    m_checkIpIsOnlineInterval = msec;}void DBHeartbeatThread::setConnectDBInterval(int msec){    m_connectDBInterval = msec;}void DBHeartbeatThread::setCheckDBHeartbeatInterval(int msec){    m_checkDBHeartbeatInterval = msec;}QString DBHeartbeatThread::lastError() const{    return m_lastError;}void DBHeartbeatThread::run(){    /// 数据库连接异常:    /// 1、断网,用checkIpIsOnline判断    /// 2、数据库服务挂了,在MySQL上测试query.exec(sql)不会阻塞,会直接就是执行失败    QSqlDatabase db;    // 打开数据库    auto openDB = [&]() -> bool {        static QString connectName = "heartbeatCheck";        if (QSqlDatabase::contains(connectName)) {            db = QSqlDatabase::database(connectName);        } else {            db = QSqlDatabase::addDatabase(m_mapDBTypeDriver.value(m_type), connectName);        }        if (db.isOpen()) {            db.close();        }        db.setHostName(m_param.ip);        db.setPort(m_param.port);        db.setDatabaseName(m_param.dbName);        db.setUserName(m_param.userName);        db.setPassword(m_param.password);        QTime startTime = QTime::currentTime();        if (db.open()) {            QTime endTime = QTime::currentTime();            qDebug() << "数据库打开成功耗时: " << startTime.msecsTo(endTime) << "ms";            return true;        }        QTime endTime = QTime::currentTime();        qDebug() << "数据库打开失败耗时: " << startTime.msecsTo(endTime) << "ms";        m_lastError = QString("数据库打开失败,原因:%1").arg(db.lastError().text());        return false;    };    // 检测数据库是否在线(执行sql操作)    auto checkDBIsOnline = [&db]() -> bool {        QSqlQuery query(db);        QString sql = "select 1";        QTime startTime = QTime::currentTime();        if (query.exec(sql)) {            return true;        }        QTime endTime = QTime::currentTime();        qDebug() << "检测心跳断开耗时:" << startTime.msecsTo(endTime) << "ms";        return false;    };    // 检测IP是否在线    auto checkIpIsOnline = [](const QString & ip) -> bool {        QProcess cmd;        QString command = "";#ifdef Q_OS_WIN        /// -n 要发送的回显请求数        /// -w 等待每次回复的超时时间(毫秒)        command = QString("ping %1 -n 1 -w 1000").arg(ip);#else        /// -s ping发送的字节数        /// -c ping的次数        command = QString("ping -s 1 -c 1 %1").arg(ip);#endif        cmd.start(command);        cmd.waitForFinished(1000 * 1);        QString retStr = cmd.readAll();        if (retStr.indexOf("ttl", 0, Qt::CaseInsensitive) == -1) {            return false;        }        return true;    };    QTimer timerCheckIp;       // 检测IP是否在线定时器    QTimer timerConnectDB;     // 连接数据库定时器    QTimer timerDBHeartbeat;   // 检测数据库心跳定时器    timerCheckIp.setInterval(m_checkIpIsOnlineInterval);    connect(&timerCheckIp, &QTimer::timeout, [&] {        timerCheckIp.stop();        if (checkIpIsOnline(m_param.ip)) {            qDebug() << "连接网络成功,正在连接数据库";            emit signal_connectedNet();            timerConnectDB.start();        } else {            timerCheckIp.start();        }    });    timerConnectDB.setInterval(m_connectDBInterval);    connect(&timerConnectDB, &QTimer::timeout, [&] {        timerConnectDB.stop();        if (checkIpIsOnline(m_param.ip)) {            if (openDB()) {                qDebug() << "连接数据库成功,开始心跳检测";                emit signal_connectedDB();                timerDBHeartbeat.start();            } else {                timerConnectDB.start();            }        } else {            qDebug() << "网络断开,正在重连";            emit signal_disconnectedNet();            timerCheckIp.start();        }    });    timerDBHeartbeat.setInterval(m_checkDBHeartbeatInterval);    connect(&timerDBHeartbeat, &QTimer::timeout, [&] {        static int checkIpFailTime = 0;        static int checkDBFailTime = 0;        timerDBHeartbeat.stop();        if (checkIpIsOnline(m_param.ip)) {            checkIpFailTime = 0;            if (checkDBIsOnline()) {                checkDBFailTime = 0;                timerDBHeartbeat.start();            } else {                ++checkDBFailTime;                if (checkDBFailTime == kCheckDBFailTime) {                    checkDBFailTime = 0;                    qDebug() << "数据库断开,正在重连";                    emit signal_disconnectedDB();                    timerConnectDB.start();                } else {                    timerDBHeartbeat.start();                }            }        } else {            ++checkIpFailTime;            if (checkIpFailTime == kCheckIpFailTime) {                checkIpFailTime = 0;                qDebug() << "网络断开,正在重连";                emit signal_disconnectedNet();                timerCheckIp.start();            } else {                timerDBHeartbeat.start();            }        }    });    timerCheckIp.start();    qDebug() << "正在连接网络";    exec();}

来源地址:https://blog.csdn.net/lin786063594/article/details/129796554

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯