Redis 连接类

RedisConnector.h

//
// Created by HP on 2024/12/27.
//

#ifndef SSUDRIVER_REDISCONNECTOR_H
#define SSUDRIVER_REDISCONNECTOR_H


#include <sw/redis++/redis++.h>
#include <iostream>
#include <thread>
#include <future>

#define ARR_SIZE(arr) ( sizeof((arr)) / sizeof((arr[0])) )

#define LOG_ERROR_WITH_EXCEPTION(e)    std::cerr <<e.what() << " in function " << __FUNCTION__ \
                                        <<" at file " << __FILE__ \
                                        <<" line " << __LINE__ <<std::endl

#define LOG_ERROR_NO_EXCEPTION     std::cerr << "Unknown exception in function " \
                                    << " in function " << __FUNCTION__ \
                                    << " at file " << __FILE__ \
                                    << " line " << __LINE__ <<std::endl

class RedisConnector {

public:

    /* 参数 readonly 用于支持主辅机的只读模式 */
    explicit RedisConnector() {

        sw::redis::ConnectionOptions connection_opts;
        connection_opts.host = "127.0.0.1";
        connection_opts.port = 6379;
        connection_opts.connect_timeout = std::chrono::milliseconds(1000);
        connection_opts.socket_timeout = std::chrono::milliseconds(50);

        new(&redis) sw::redis::Redis{connection_opts};

        if (!isValid()) {
            throw std::runtime_error("Redis connection failed.");
        }

    }

    ~RedisConnector() {
        stop();
    }

    void publish(const std::string &topic, const std::string &message) {

        try {
            redis.publish(topic, message);
        } catch (const sw::redis::Error &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (const std::exception &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (...) {
            LOG_ERROR_NO_EXCEPTION;
        }

    }

    void setValue(const std::string &BigKey, const std::string &key, const std::string &value) {
        try {
            redis.hset(BigKey, key, value);
        } catch (const sw::redis::Error &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (const std::exception &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (...) {
            LOG_ERROR_NO_EXCEPTION;
        }
    }

    void setValues(const std::string &BigKey, std::vector<std::tuple<std::string, std::string>> &value) {

        try {
            redis.hmset(BigKey, value.cbegin(), value.cend());
        } catch (const sw::redis::Error &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (const std::exception &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (...) {
            LOG_ERROR_NO_EXCEPTION;
        }

    }

    void setValues(const std::string &BigKey, std::vector<std::tuple<std::string, std::string>> &&value) {

        try {
            redis.hmset(BigKey, value.cbegin(), value.cend());
        } catch (const sw::redis::Error &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (const std::exception &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
        } catch (...) {
            LOG_ERROR_NO_EXCEPTION;
        }

    }

    void setValuesOn(const std::string &BigKey, const std::map<std::string, std::string> &input_value) {

        std::map<std::string, std::string> key_value;

        for (const auto &i: input_value) {
            key_value.emplace("comstatus" + i.first.substr(std::string{"ssu"}.size()), "255");
        }


        redis.hmset(BigKey, key_value.cbegin(), key_value.cend());
    }

    void setValuesOff(const std::string &BigKey, const std::map<std::string, std::string> &input_value) {

        std::map<std::string, std::string> key_value;

        for (const auto &i: input_value) {
            key_value.emplace("comstatus" + i.first.substr(std::string{"ssu"}.size()), "0");
        }


        redis.hmset(BigKey, key_value.cbegin(), key_value.cend());
    }

    // 启动订阅线程
    void startSubscribe(const std::vector<std::string> &channels,
                        const std::function<void(const std::string &, const std::string &)> &callback) {
        // 如果线程已在运行,先停止它
        if (subscriberThread && subscriberThread->joinable()) {
            stop();
        }

        // 重置停止标志
        stopFlag = false;

        // 启动新线程
        subscriberThread = std::make_unique<std::thread>(
                &RedisConnector::subscribeOnMessage, this,
                channels, callback, std::cref(stopFlag)
        );

    }


    // 停止订阅线程
    void stop() {

        if (!subscriberThread) return;

        // 设置停止标志
        stopFlag = true;

        // 等待线程结束
        if (subscriberThread->joinable()) {
            subscriberThread->join();
        }

        subscriberThread.reset();
    }


    bool isValid(int timeout_ms = 500l) {

        try {
            auto start_time = std::chrono::high_resolution_clock::now();
            auto end_time = start_time + std::chrono::milliseconds(timeout_ms);

            while (true) {
                auto now = std::chrono::high_resolution_clock::now();
                if (now >= end_time) {
                    return false;
                }

                auto ans = redis.ping();
                if (ans == "PONG") {
                    return true;
                }

                std::this_thread::sleep_for(std::chrono::microseconds(100));
            }
        } catch (const std::exception &e) {
            LOG_ERROR_WITH_EXCEPTION(e);
            return false;
        } catch (...) {
            LOG_ERROR_NO_EXCEPTION;
            return false;
        }

    }


private:

    void subscribeOnMessage(const std::vector<std::string> &channels,
                            const std::function<void(const std::string &channel, const std::string &msg)> &callback,
                            const std::atomic<bool> &stop) {

        if (!callback) {
            throw std::runtime_error("Callback function object is empty.");
        }


        auto sub = redis.subscriber();
        sub.on_message(callback);

        /*订阅频道*/
        sub.subscribe(channels.begin(), channels.end());


        while (!stop) {


            try {

                sub.consume();

            } catch (const sw::redis::ReplyError &replyError) {

                ///@see https://github.com/sewenew/redis-plus-plus?tab=readme-ov-file#exception-1

                LOG_ERROR_WITH_EXCEPTION(replyError);
                continue;

            } catch (const sw::redis::TimeoutError &timeoutError) {
                continue;
            } catch (const sw::redis::Error &e) {
                LOG_ERROR_WITH_EXCEPTION(e);
            } catch (const std::exception &e) {
                /* 重新抛出Redis Plus Plus未处理的异常,希望上层调用者处理 */
                LOG_ERROR_WITH_EXCEPTION(e);
                throw e;
            } catch (...) {
                /* 遇到未知异常,停止执行回调函数并打印日志 */
                LOG_ERROR_NO_EXCEPTION;
                return;
            }
        }

    }

    union {
        sw::redis::Redis redis;
    };

    std::unique_ptr<std::thread> subscriberThread;

    std::atomic<bool> stopFlag{};

};


#endif //SSUDRIVER_REDISCONNECTOR_H

posted @ 2025-10-09 13:57  BlackSnow  阅读(8)  评论(0)    收藏  举报