You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Redisson+Redis Sentinel:故障转移与写入异常问题求助

Redisson与Redis Sentinel集群问题排查与解决方案

我来帮你一步步拆解这些问题,结合你的代码和Redis Sentinel的工作机制,逐个分析解决:

问题1:Redisson将3个节点都识别为Slave

可能原因

  1. Sentinel主节点名称不匹配:你代码里设置的setMasterName("redis-cluster")必须和Redis Sentinel集群中配置的主节点名称完全一致。如果Sentinel里的主节点是默认的mymaster或者其他名称,Redisson就无法定位到主节点,会把所有节点误判为Slave。
  2. Sentinel通信异常:可能是防火墙阻挡了Java程序与Sentinel节点的26379端口通信,或者Sentinel本身状态异常,导致Redisson无法从Sentinel获取到主节点的地址和角色信息。
  3. Redisson配置缺失关键逻辑:比如没有正确初始化Sentinel连接,导致Redisson无法完成集群拓扑的探测。

解决方案

  • 先验证Sentinel的主节点名称:登录任意Sentinel节点,执行命令 sentinel masters,查看输出中的name字段,确保和代码里的redis-cluster完全一致。如果不一致,修改代码中的setMasterName参数或者调整Sentinel的配置文件。
  • 检查网络连通性:用telnet 192.168.56.101 26379测试Java程序所在机器能否访问Sentinel节点的端口,确保没有防火墙或网络策略限制。
  • 开启Redisson调试日志:添加SLF4J+Logback日志配置,把日志级别设为DEBUG,查看Redisson与Sentinel交互的日志,确认是否成功获取到主节点的信息。

问题2:创建的键值对未存储到Redis中

核心原因

你的主线程在提交异步任务后,立刻执行了e.shutdown()client.shutdown()node.shutdown()——RExecutorServiceexecute()是异步执行的,主线程不会等待任务完成就直接关闭了客户端,导致所有Redis操作被中断,数据根本没机会写入。

解决方案

修改主线程逻辑,等待异步任务执行完成后再关闭资源:

public static void main(String[] args) throws InterruptedException {
    Config config = new Config();
    // 保留原有的config配置...
    
    RedissonClient client = Redisson.create(config);
    RedissonNodeConfig nodeConfig = new RedissonNodeConfig(config);
    nodeConfig.setExecutorServiceWorkers(Collections.singletonMap("myExecutor6", 1));
    RedissonNode node = RedissonNode.create(nodeConfig);
    node.start();
    System.out.println("Node address "+node.getRemoteAddress().toString());
    
    RExecutorService e = client.getExecutorService("myExecutor6");
    // 提交任务并获取Future,用于等待任务完成
    Future<?> future = e.submit(new RunnableTask());
    
    // 等待任务执行完成,设置合理的超时时间(比如300秒)
    future.awaitTermination(300, TimeUnit.SECONDS);
    
    e.shutdown();
    // 等待executor完全关闭
    e.awaitTermination(10, TimeUnit.SECONDS);
    e.delete();
    
    client.shutdown();
    node.shutdown();
    System.out.println("Hello World!" );
}

同时在RunnableTaskrun()方法中添加异常捕获,避免任务因异常无声失败:

@Override
public void run(){
    try {
        // 保留原有的任务逻辑...
    } catch (Exception ex) {
        ex.printStackTrace();
        // 这里可以添加自定义异常处理,比如记录告警日志
    }
}

问题3:主节点故障后程序挂起退出,无法自动切换

可能原因

  1. 任务未处理连接异常:主节点故障时,Redisson会尝试重连,但如果任务代码没有捕获连接异常,会直接终止任务,导致程序退出。
  2. Redisson配置参数不合理:当前setTimeout(60000)设置过长,故障转移期间程序会等待超时后才触发重试;RetryAttemptsRetryInterval的配置也可能不足以覆盖故障转移的时间窗口。
  3. 主线程过早关闭资源:和问题2一样,如果客户端被提前关闭,Redisson就无法完成故障转移后的自动重连。

解决方案

  1. 优化Redisson配置参数:调整参数加快故障检测和重连速度:
config.useSentinelServers()
    .setMasterName("redis-cluster")
    .addSentinelAddress("192.168.56.101:26379")
    .addSentinelAddress("192.168.56.102:26379")
    .addSentinelAddress("192.168.56.103:26379")
    .setPingTimeout(100)
    .setTimeout(10000) // 缩短超时时间,加快故障检测
    .setRetryAttempts(10) // 调整合理的重试次数
    .setReconnectionTimeout(5000) // 缩短重连间隔
    .setRetryInterval(1000)
    .setReadMode(ReadMode.SLAVE)
    .setConnectTimeout(5000) // 缩短连接超时
    .setSubscriptionMode(SubscriptionMode.MASTER)
    .setMasterConnectionPoolSize(32) // 设置合理的连接池大小
    .setSlaveConnectionPoolSize(32);
  1. 在任务中添加异常处理与重试逻辑:确保任务遇到连接异常时不会直接终止:
@Override
public void run(){
    System.out.println("I am in ..");
    RMap<String, String> map = client.getMap("completeNewMap");
    System.out.println("is thread interrupted?? " + Thread.currentThread().isInterrupted());
    
    NodesGroup ngroup = client.getNodesGroup();
    Collection<Node> nodes = ngroup.getNodes();
    for(Node node : nodes){
        System.out.println("Node ip "+ node.getAddr().toString()+" type: "+node.getType().toString());
    }
    
    for(int i=0; i < 10000; i++) {
        try {
            String key = "bg_key_"+String.valueOf(i);
            String value = String.valueOf(UUID.randomUUID());
            String oldVal = map.get(key);
            map.put(key, value);
            RBucket<String> bck = client.getBucket(key);
            bck.set(value);
            System.out.println("I am going to replace the old value " + oldVal + " with new value " + value + " at key "+key);
        } catch (RedisConnectionException e) {
            System.err.println("Connection error occurred, retrying: " + e.getMessage());
            try {
                Thread.sleep(500);
                i--; // 重试当前key的写入
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    System.out.println("I am outta here!!");
}
  1. 保持客户端长期运行:如果是持续服务的程序,RedissonClient的生命周期应该和程序一致,不要在任务执行过程中随意关闭,这样故障转移时Redisson才能自动完成新主节点的切换。

内容的提问来源于stack exchange,提问作者Gioachino Bartolotta

火山引擎 最新活动