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

浙公网安备 33010602011771号