[QUESTION] 哨兵模式下,redis发生主备切换后,redis-plus-plus中的Redis对象会自动连接到新的master节点吗?
jdkuangxx opened this issue · 4 comments
Describe the problem
哨兵模式下,redis发生主备切换后,Redis对象会自动连接到新的master节点吗?
Environment:
- OS: centos7
- hiredis version: v1.2.0
- redis-plus-plus version: master
我写了一个简单的demo来测试”Redis对象是否可以自动切换到新的master“,下边是我的demo以及测试步骤:
#include <sw/redis++/redis++.h>
#include <vector>
#include <iostream>
using namespace sw::redis;
int main(int argc, char** argv) {
try {
SentinelOptions sentinel_opts;
sentinel_opts.nodes = {
{"127.0.0.1", 7003},
{"127.0.0.1", 7004},
{"127.0.0.1", 7005}
};
sentinel_opts.connect_timeout = std::chrono::milliseconds(100);
sentinel_opts.socket_timeout = std::chrono::milliseconds(100);
auto sentinel = std::make_shared<Sentinel>(sentinel_opts);
ConnectionOptions connection_opts;
connection_opts.password = "123456";
connection_opts.connect_timeout = std::chrono::milliseconds(100); // Required.
connection_opts.socket_timeout = std::chrono::milliseconds(100); // Required.
ConnectionPoolOptions pool_opts;
// Optional. The default size is 1.
pool_opts.size = 3;
// Connect to master node.
auto redis = Redis(sentinel, "mymaster", Role::MASTER, connection_opts, pool_opts);
redis.set("key", "val");
auto val = redis.get("key");
std::vector<std::string> vec = {"a", "b", "c"};
redis.rpush("list", vec.begin(), vec.end());
// Redis LIST to std::vector<std::string>.
std::vector<std::string> res;
redis.lrange("list", 0, -1, std::back_inserter(res));
redis.hset("hash", "field", "val");
} catch (const Error &e) {
std::cout << e.what() << std::endl;
}
}
测试步骤:
- 使用gdb在
try
代码块的最后一行打一个断点,然后运行 - 关闭master节点,触发主备切换
- 在确认主备切换完成后(如下面两张图片所示),继续运行程序,此时会出现
Failed to get reply: Server closed the connection
的错误(是我的使用姿势有问题吗,有没有相关文档可以参考一下)
在确认主备切换完成后(如下面两张图片所示),继续运行程序,此时会出现Failed to get reply: Server closed the connection的错误(是我的使用姿势有问题吗,有没有相关文档可以参考一下
这个时候redis-plus-plus并不会自动感知到主备切换了,而上一次链接是能够成功发送请求到,所以它会认为该链接是正常的,于是就会继续向原来的节点发送请求,所以你会捕获一个异常,这时可以重试一下这个请求,redis-plus-plus发现链接断了,然后就会尝试重新从sentinel获取最新的master的地址,因此重试的请求就可以发送成功了,并且会更新本地的master信息,后续的请求都会发给新的master。
在确认主备切换完成后(如下面两张图片所示),继续运行程序,此时会出现Failed to get reply: Server closed the connection的错误(是我的使用姿势有问题吗,有没有相关文档可以参考一下
这个时候redis-plus-plus并不会自动感知到主备切换了,而上一次链接是能够成功发送请求到,所以它会认为该链接是正常的,于是就会继续向原来的节点发送请求,所以你会捕获一个异常,这时可以重试一下这个请求,redis-plus-plus发现链接断了,然后就会尝试重新从sentinel获取最新的master的地址,因此重试的请求就可以发送成功了,并且会更新本地的master信息,后续的请求都会发给新的master。
ok,我理解你的意思了,是不是类似于这种形式:
try {
redis.hset("hash", "field", "val");
} catch (const ClosedError &err) {
std::cout << err.what() << std::endl;
// reconnect and retry
redis = Redis(sentinel, "mymaster", Role::MASTER, connection_opts, pool_opts);
redis.hset("hash", "field", "val");
} catch (const std::exception& err) {
std::cout << err.what() << std::endl;
}
不是,不需要重新创建Redis链接,直接重试你的命令就可以了,另外,出了ClosedError,还有可能是IoError:
try {
redis.hset("hash", "field", "val");
} catch (const Error &err) {
std::cout << err.what() << std::endl;
// retry
redis.hset("hash", "field", "val");
} catch (const IoError &e) {
redis.hset("hash", "field", "val");
} catch (const std::exception& err) {
std::cout << err.what() << std::endl;
}
不过,通常可以考虑直接把异常抛给应用程序,让应用程序来决定是否重试
不是,不需要重新创建Redis链接,直接重试你的命令就可以了,另外,出了ClosedError,还有可能是IoError:
try { redis.hset("hash", "field", "val"); } catch (const Error &err) { std::cout << err.what() << std::endl; // retry redis.hset("hash", "field", "val"); } catch (const IoError &e) { redis.hset("hash", "field", "val"); } catch (const std::exception& err) { std::cout << err.what() << std::endl; }
不过,通常可以考虑直接把异常抛给应用程序,让应用程序来决定是否重试
明白了,感谢!!!