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修改相应的路径即可


2018-09-22 redis desktop windows 相关_第1张图片
  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编译路径


2018-09-22 redis desktop windows 相关_第2张图片
  1. Open ./src/rdm.pro in Qt Creator
    阿弥陀佛,终于编译完成


    2018-09-22 redis desktop windows 相关_第3张图片
  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(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 m_qmlUtils; 工具类
  • QSharedPointer m_connections; 用于与redis进行通信使用
  • QSharedPointer m_updater;查询新版本
  • QSharedPointer m_keyValues;
  • QSharedPointer m_formattersManager;
  • QSharedPointer m_bulkOperations;
  • QSharedPointer m_consoleModel;
  • QSharedPointer m_serverStatsModel;
  • QSharedPointer 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");
    m_logger = el::Helpers::logDispatchCallback("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("Command");
    qRegisterMetaType("RedisClient::Command");
    qRegisterMetaType("Response");
    qRegisterMetaType("RedisClient::Response");
    qRegisterMetaType>("QVector");
    qRegisterMetaType("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(new TabViewModel(getTabModelFactory()));

    connect(m_connections.data(), &ConnectionsManager::openConsole,
            m_consoleModel.data(), &TabViewModel::openTab);

    m_serverStatsModel = QSharedPointer(new TabViewModel(getTabModelFactory()));

    connect(m_connections.data(), &ConnectionsManager::openServerStats,
            this, [this](QSharedPointer c)
    {
        m_serverStatsModel->openTab(c);
    });

    m_formattersManager = QSharedPointer(new ValueEditor::FormattersManager());
    m_formattersManager->loadFormatters();

    m_consoleAutocompleteModel = QSharedPointer(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

你可能感兴趣的:(2018-09-22 redis desktop windows 相关)