Redisson+Redis Sentinel:故障转移与写入异常问题求助
Redisson与Redis Sentinel集群问题排查与解决方案
我来帮你一步步拆解这些问题,结合你的代码和Redis Sentinel的工作机制,逐个分析解决:
问题1:Redisson将3个节点都识别为Slave
可能原因
- Sentinel主节点名称不匹配:你代码里设置的
setMasterName("redis-cluster")必须和Redis Sentinel集群中配置的主节点名称完全一致。如果Sentinel里的主节点是默认的mymaster或者其他名称,Redisson就无法定位到主节点,会把所有节点误判为Slave。 - Sentinel通信异常:可能是防火墙阻挡了Java程序与Sentinel节点的26379端口通信,或者Sentinel本身状态异常,导致Redisson无法从Sentinel获取到主节点的地址和角色信息。
- 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()——RExecutorService的execute()是异步执行的,主线程不会等待任务完成就直接关闭了客户端,导致所有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!" ); }
同时在RunnableTask的run()方法中添加异常捕获,避免任务因异常无声失败:
@Override public void run(){ try { // 保留原有的任务逻辑... } catch (Exception ex) { ex.printStackTrace(); // 这里可以添加自定义异常处理,比如记录告警日志 } }
问题3:主节点故障后程序挂起退出,无法自动切换
可能原因
- 任务未处理连接异常:主节点故障时,Redisson会尝试重连,但如果任务代码没有捕获连接异常,会直接终止任务,导致程序退出。
- Redisson配置参数不合理:当前
setTimeout(60000)设置过长,故障转移期间程序会等待超时后才触发重试;RetryAttempts和RetryInterval的配置也可能不足以覆盖故障转移的时间窗口。 - 主线程过早关闭资源:和问题2一样,如果客户端被提前关闭,Redisson就无法完成故障转移后的自动重连。
解决方案
- 优化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);
- 在任务中添加异常处理与重试逻辑:确保任务遇到连接异常时不会直接终止:
@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!!"); }
- 保持客户端长期运行:如果是持续服务的程序,RedissonClient的生命周期应该和程序一致,不要在任务执行过程中随意关闭,这样故障转移时Redisson才能自动完成新主节点的切换。
内容的提问来源于stack exchange,提问作者Gioachino Bartolotta




