1.为什么
- redis相关学习
- 了解redisdesktop代码架构
- 学习c++下面如何优雅地与redis进行数据通信
2.是什么
Open source cross-platform Redis Desktop Manager based on Qt 5
跨平台的基于qt5的redis桌面控制端
3.编译安装【参考官网】
3.1、环境相关
windows 10
visual studio 2015[自行安装即可]
qt5.9.2【qt官网旧版本地址】
git
cmake【用于编译libssh2】
3.2、下载源码
git clone --recursive https://github.com/uglide/RedisDesktopManager.git -b 0.9 rdm
3.3、官网步骤
Build on Windows
- Install Visual Studio 2015 Community with Updates
- Install Qt 5.9
- Install Win32 Openssl 1.0.X
第一个坑,需要安装1.0.x版本,安装1.1.X之后报缺少相应的动态库。
第二个坑,安装的路径不要出现空格等异常字符,会导致找不到相关的动态库,默认安装C:\OpenSSL-Win32即可,如果安装在其他目录,编译文件rds\RedisDesktopManager\3rdparty\qredisclient\3rdparty\qsshclient\3rdparty\3rdparty.pri修改相应的路径即可
- Install CMake
- Build
libssh2
library in folder3rdparty/qredisclient/3rdparty/qsshclient/3rdparty/libssh2
using CMake
编译步骤:
1、进入libssh2目录
2、mkdir bin
3、cd bin
4、cmake .. 【等待完成】
5、cmake --build . 【等待完成】
第一个坑, 由于git下来之后目录太长,导致文件文全路径超过了256,将libssh2移到较短的路径下面即可,如果移动了,记得编译完成之后拷回原先的文件夹中
第二个坑,编译生产的libssh2实际在目录libssh2\bin\src\Debug下面,需要手动修改
rds\RedisDesktopManager\3rdparty\qredisclient\3rdparty\qsshclient\3rdparty\3rdparty.pri编译路径
-
Open ./src/rdm.pro in Qt Creator
阿弥陀佛,终于编译完成
Run build
4.源码研究
4.1异常处理
main函数中有下面的一段代码
QFileInfo appPath(QString::fromLocal8Bit(argv[0]));
QString appDir(appPath.absoluteDir().path());
QString crashReporterPath = QString("%1/crashreporter").arg(appDir.isEmpty() ? "." : appDir);
CrashHandler::instance()->Init(QDir::homePath(), appPath.absoluteFilePath(), crashReporterPath);
字面意思理解应该是异常崩溃处理,刚好是之前工作中遇到的,之前在公司中使用的是处理
windows下面异常奔溃处理,印象中是使用的minidump相关的东西
linux下面是core dump相关的东西,印象中是可以使用命令ulimit开启,也可以在程序中通过setrlimit系统调用开启,然后使用gdb进行数据查看
且看这边是如何使用的。
单例模式的CrashHandler,具体的实现在CrashHandlerPrivate中,qt中典型的代码模式。
class CrashHandlerPrivate;
class CrashHandler
{
public:
static CrashHandler* instance();
//初始化函数
void Init(const QString& reportPath, const QString &appPath, const QString &crashReporterFullPath);
void setReportCrashesToSystem(bool report);
bool writeMinidump();
private:
CrashHandler();
~CrashHandler();
Q_DISABLE_COPY(CrashHandler)
CrashHandlerPrivate* d;
};
init函数调用直接调用CrashHandlerPrivate::InitCrashHandler函数,且仔细看来
void CrashHandlerPrivate::InitCrashHandler(const QString& dumpPath, const QString& appPath, const QString& crashReporterFullPath)
{
if ( pHandler != NULL )
return;
wcscpy(applicationPath, appPath.toStdWString().c_str());
wcscpy(crashReporterPath, crashReporterFullPath.toStdWString().c_str());
#if defined(Q_OS_WIN32)
std::wstring pathAsStr = (const wchar_t*)dumpPath.utf16();
pHandler = new google_breakpad::ExceptionHandler(
pathAsStr,
/*FilterCallback*/ 0,
DumpCallback,
/*context*/
0,
true
);
#elif defined(Q_OS_LINUX)
std::string pathAsStr = dumpPath.toStdString();
google_breakpad::MinidumpDescriptor md(pathAsStr);
pHandler = new google_breakpad::ExceptionHandler(
md,
/*FilterCallback*/ 0,
DumpCallback,
/*context*/ 0,
true,
-1
);
#elif defined(Q_OS_MAC)
std::string pathAsStr = dumpPath.toStdString();
pHandler = new google_breakpad::ExceptionHandler(
pathAsStr,
/*FilterCallback*/ 0,
DumpCallback,
/*context*/
0,
true,
NULL
);
#endif
}
使用的是google breakpad相关异常处理,貌似之前在哪边看到过,搜索一下详细介绍google breakpad, 以后项目中可以考虑。
4.2main函数继续
主程序这种Application
Application a(argc, argv);
a.initModels();
a.initQml();
Application继承于QApplication,
构造函数
Application::Application(int &argc, char **argv)
: QApplication(argc, argv),
m_engine(this),
m_qmlUtils(QSharedPointer<QmlUtils>(new QmlUtils())),
m_logger(nullptr)
{
// Init components required for models and qml
initLog();
initAppInfo();
processCmdArgs();
initAppFonts();
initRedisClient();
initUpdater();
installTranslator();
}
4.2.1几个成员变量
- QQmlApplicationEngine m_engine; qt中定义QQmlApplicationEngine
- QSharedPointer<QmlUtils> m_qmlUtils; 工具类
- QSharedPointer<ConnectionsManager> m_connections; 用于与redis进行通信使用
- QSharedPointer<Updater> m_updater;查询新版本
- QSharedPointer<ValueEditor::TabsModel> m_keyValues;
- QSharedPointer<ValueEditor::FormattersManager> m_formattersManager;
- QSharedPointer<BulkOperations::Manager> m_bulkOperations;
- QSharedPointer<TabViewModel> m_consoleModel;
- QSharedPointer<TabViewModel> m_serverStatsModel;
- QSharedPointer<Console::AutocompleteModel> m_consoleAutocompleteModel;
- LogHandler* m_logger;
使用的第三方的库 easylogging++
class LogHandler : public QObject, public el::LogDispatchCallback
void Application::initLog()
{
el::Configurations defaultConf;
defaultConf.setToDefault();
defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
defaultConf.setGlobally(el::ConfigurationType::ToFile, "false");
el::Loggers::reconfigureLogger("default", defaultConf);
el::Loggers::removeFlag(el::LoggingFlag::NewLineForContainer);
el::Helpers::installLogDispatchCallback<LogHandler>("LogHandler");
m_logger = el::Helpers::logDispatchCallback<LogHandler>("LogHandler");
if (!m_logger) {
LOG(ERROR) << "App log: ERROR";
} else {
LOG(INFO) << "App log init: OK";
}
}
上述是日志初始化。
QString m_settingsDir; 保存连接redis的参数
QString m_renderingBackend;是否renderingBackend
4.2.2构造函数
4.2.2.1 initLog();日志初始化,见上
4.2.2.2 initAppInfo() 设置一些application的信息
4.2.2.3 processCmdArgs(); 处理命令行的参数
void Application::processCmdArgs()
{
QCommandLineParser parser;
QCommandLineOption settingsDir(
"settings-dir",
"(Optional) Directory where RDM looks/saves .rdm directory with connections.json file",
"settingsDir",
QDir::homePath()
);
QCommandLineOption renderingBackend(
"rendering-backend",
"(Optional) QML rendering backend [software|opengl|d3d12|'']",
"renderingBackend",
"auto"
);
parser.addHelpOption();
parser.addVersionOption();
parser.addOption(settingsDir);
parser.addOption(renderingBackend);
parser.process(*this);
m_settingsDir = parser.value(settingsDir);
m_renderingBackend = parser.value(renderingBackend);
}
QCommandLineOption 之前未曾使用过,用于解析命令行参数的内容,gei it
4.2.2.4 initAppFonts() 设置字体参数
4.2.2.5 initRedisClient()初始化redisclient
inline void initRedisClient()
{
qRegisterMetaType<RedisClient::Command>("Command");
qRegisterMetaType<RedisClient::Command>("RedisClient::Command");
qRegisterMetaType<RedisClient::Response>("Response");
qRegisterMetaType<RedisClient::Response>("RedisClient::Response");
qRegisterMetaType<QVector<QVariant*>>("QVector<QVariant*>");
qRegisterMetaType<QVariant*>("QVariant*");
}
只是注册了一些redisclient的一些数据结构,连接redis,使用了qredisclient
4.2.2.6 initUpdater()初始化查询是否有新版本,有新版本告警。
4.2.2.7installTranslator()用于国际化。
4.2.3 initModels()初始化数据项
void Application::initModels()
{
//1、设置连接redis参数,管理链接,初始化m_keyValues,记录redis的一些关键参数信息TabsModel;初始化m_connections;初始化m_bulkOperations
initConnectionsManager();
//2、初始化m_consoleModel
m_consoleModel = QSharedPointer<TabViewModel>(new TabViewModel(getTabModelFactory<Console::Model>()));
connect(m_connections.data(), &ConnectionsManager::openConsole,
m_consoleModel.data(), &TabViewModel::openTab);
m_serverStatsModel = QSharedPointer<TabViewModel>(new TabViewModel(getTabModelFactory<ServerStats::Model>()));
connect(m_connections.data(), &ConnectionsManager::openServerStats,
this, [this](QSharedPointer<RedisClient::Connection> c)
{
m_serverStatsModel->openTab(c);
});
m_formattersManager = QSharedPointer<ValueEditor::FormattersManager>(new ValueEditor::FormattersManager());
m_formattersManager->loadFormatters();
m_consoleAutocompleteModel = QSharedPointer<Console::AutocompleteModel>(new Console::AutocompleteModel());
}
4.2.4 initQml()初始化QML
void Application::initQml()
{
if (m_renderingBackend == "auto") {
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
// Use software renderer on Windows & Linux by default
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
#endif
} else {
QQuickWindow::setSceneGraphBackend(m_renderingBackend);
}
registerQmlTypes();
registerQmlRootObjects();
try {
//加载界面qml文件
m_engine.load(QUrl(QStringLiteral("qrc:///app.qml")));
} catch (...) {
qDebug() << "Failed to load app window. Retrying with software renderer...";
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
m_engine.load(QUrl(QStringLiteral("qrc:///app.qml")));
}
qDebug() << "Rendering backend:" << QQuickWindow::sceneGraphBackend();
}
5 第三方库相关
阅读源码的时候发现很多的第三方库,有些之前听说过,但是具体的使用确不知道,有些实际就没有听说过,还是需要多看多记。