背景
在QT中做有关于MySQL的业务时,有时候需要知道MySQL数据库的当前连接状态。但QT在提供的QSqlDatabase模块中并没有相关的功能,不像QTcpSocket在连接服务或断开服务时都有相应的信号发出。
网上方法
- (1)使用QSqlDatabase::isOpen()判断
经测试,在突然与数据库所在网络断开后,该接口仍返回true,不靠谱。
- (2)设置连接参数setConnectOptions(“MYSQL_OPT_RECONNECT=1”)
未测试,看网上的说法是在长时间未操作(默认8小时)数据库则会与数据库断开,如设置这个参数则会重连,但不知道在断网后的情况怎样。
- (3)定时执行sql语句来判断
使用定时器,定时执行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 ¶m, QObject *parent = nullptr);public: void setDBTypeAndConnectParm(const DBType &type, const ConnectParam ¶m); 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 ¶m, QObject *parent) : DBHeartbeatThread(parent){ m_type = type; m_param = param;}void DBHeartbeatThread::setDBTypeAndConnectParm(const DBHeartbeatThread::DBType &type, const DBHeartbeatThread::ConnectParam ¶m){ 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