sewenew / redis-plus-plus

Redis client written in C++

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[QUESTION] 哨兵模式下,redis发生主备切换后,redis-plus-plus中的Redis对象会自动连接到新的master节点吗?

jdkuangxx opened this issue · 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;
    }
}

测试步骤:

  1. 使用gdb在try代码块的最后一行打一个断点,然后运行
  2. 关闭master节点,触发主备切换
  3. 在确认主备切换完成后(如下面两张图片所示),继续运行程序,此时会出现Failed to get reply: Server closed the connection的错误(是我的使用姿势有问题吗,有没有相关文档可以参考一下)

b
a

在确认主备切换完成后(如下面两张图片所示),继续运行程序,此时会出现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;
}

不过,通常可以考虑直接把异常抛给应用程序,让应用程序来决定是否重试

明白了,感谢!!!