2018-09-22 redis desktop windows 相关

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

  1. Install Visual Studio 2015 Community with Updates
  2. Install Qt 5.9
  3. Install Win32 Openssl 1.0.X

第一个坑,需要安装1.0.x版本,安装1.1.X之后报缺少相应的动态库。

第二个坑,安装的路径不要出现空格等异常字符,会导致找不到相关的动态库,默认安装C:\OpenSSL-Win32即可,如果安装在其他目录,编译文件rds\RedisDesktopManager\3rdparty\qredisclient\3rdparty\qsshclient\3rdparty\3rdparty.pri修改相应的路径即可


  1. Install CMake
  2. Build libssh2 library in folder 3rdparty/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编译路径


  1. Open ./src/rdm.pro in Qt Creator
    阿弥陀佛,终于编译完成


  2. 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

参见easylogging++详细讲解

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 第三方库相关

阅读源码的时候发现很多的第三方库,有些之前听说过,但是具体的使用确不知道,有些实际就没有听说过,还是需要多看多记。

5.1 easyloging++

5.2 google breadpad

5.3 qredisclient

5.4 AsyncFuture

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • Qt是属于一个跨平台的GUI开发软件,支持的平台有Unix、Linux、Windows/WinCE、IOS等。 Q...
    一叶之界阅读 8,162评论 0 17
  • 说明本次redis集群安装在rhel6.8 64位机器上,redis版本为3.2.8,redis的gem文件版本为...
    读或写阅读 14,739评论 3 9
  • 回崇明(上·长篇小说连载三十二)作者 沈姜当小姨阿英从厢房里出来,准备去房后拿些柴草时,黑油卫国眼睛使劲嘌望着小姨...
    姜苏阅读 126评论 0 0
  • 有一天,你突然想要学吉他。于是你兴致勃勃买了吉他,跟着网络教程自学。然后没过几天便开始懈怠,渐渐地练习吉他的次数越...
    無湦阅读 203评论 0 0
  • 1、 我可以帮拿到比别的公司利息低的贷款; 2、 相同利息我可以帮你拿到高额度的贷款,甚至比银行的还要高; 3、 ...
    南山石_0d4e阅读 178评论 0 0