在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_OBJECT
public:
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();
}