1.為什么
- redis相關學習
- 了解redisdesktop代碼架構
- 學習c++下面如何優(yōu)雅地與redis進行數(shù)據(jù)通信
2.是什么
Open source cross-platform Redis Desktop Manager based on Qt 5
跨平臺的基于qt5的redis桌面控制端
3.編譯安裝【參考官網】
3.1、環(huán)境相關
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之后報缺少相應的動態(tài)庫。
第二個坑,安裝的路徑不要出現(xiàn)空格等異常字符,會導致找不到相關的動態(tài)庫,默認安裝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函數(shù)中有下面的一段代碼
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系統(tǒng)調用開啟,然后使用gdb進行數(shù)據(jù)查看
且看這邊是如何使用的。
單例模式的CrashHandler,具體的實現(xiàn)在CrashHandlerPrivate中,qt中典型的代碼模式。
class CrashHandlerPrivate;
class CrashHandler
{
public:
static CrashHandler* instance();
//初始化函數(shù)
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函數(shù)調用直接調用CrashHandlerPrivate::InitCrashHandler函數(shù),且仔細看來
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函數(shù)繼續(xù)
主程序這種Application
Application a(argc, argv);
a.initModels();
a.initQml();
Application繼承于QApplication,
構造函數(shù)
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的參數(shù)
QString m_renderingBackend;是否renderingBackend
4.2.2構造函數(shù)
4.2.2.1 initLog();日志初始化,見上
4.2.2.2 initAppInfo() 設置一些application的信息
4.2.2.3 processCmdArgs(); 處理命令行的參數(shù)
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 之前未曾使用過,用于解析命令行參數(shù)的內容,gei it
4.2.2.4 initAppFonts() 設置字體參數(shù)
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的一些數(shù)據(jù)結構,連接redis,使用了qredisclient
4.2.2.6 initUpdater()初始化查詢是否有新版本,有新版本告警。
4.2.2.7installTranslator()用于國際化。
4.2.3 initModels()初始化數(shù)據(jù)項
void Application::initModels()
{
//1、設置連接redis參數(shù),管理鏈接,初始化m_keyValues,記錄redis的一些關鍵參數(shù)信息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 第三方庫相關
閱讀源碼的時候發(fā)現(xiàn)很多的第三方庫,有些之前聽說過,但是具體的使用確不知道,有些實際就沒有聽說過,還是需要多看多記。