第10集Java实战深入探讨Redis的哨兵机制
|字数总计:5.5k|阅读时长:25分钟|阅读量:
引言
Redis哨兵(Sentinel)是Redis官方提供的高可用性解决方案,它能够监控Redis主从节点的健康状态,并在主节点故障时自动进行故障转移。哨兵机制不仅提供了自动故障检测和转移功能,还支持配置提供和通知功能。本文将深入探讨Redis哨兵机制的原理、配置和Java实战应用,帮助开发者构建高可用的Redis集群。
Redis哨兵机制概述
什么是哨兵机制
Redis哨兵是一个独立的进程,用于监控Redis主从节点的状态,主要功能包括:
- 监控:持续监控主从节点的健康状态
- 通知:当监控的节点出现故障时,通过API通知管理员
- 自动故障转移:当主节点故障时,自动将从节点提升为主节点
- 配置提供:为客户端提供当前主节点的地址
哨兵架构模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class SentinelArchitecture { public void demonstrateSentinelArchitecture() { System.out.println("Redis哨兵架构:"); System.out.println("┌─────────────┐ ┌─────────────┐ ┌─────────────┐"); System.out.println("│ Sentinel1 │ │ Sentinel2 │ │ Sentinel3 │"); System.out.println("└─────────────┘ └─────────────┘ └─────────────┘"); System.out.println(" │ │ │"); System.out.println(" └───────────────────┼───────────────────┘"); System.out.println(" │"); System.out.println("┌─────────────┐ ┌─────────────┐ ┌─────────────┐"); System.out.println("│ Master │◄───│ Slave1 │ │ Slave2 │"); System.out.println("│ Redis │ │ Redis │ │ Redis │"); System.out.println("└─────────────┘ └─────────────┘ └─────────────┘"); System.out.println(" │"); System.out.println(" ┌─────────────┐"); System.out.println(" │ Client │"); System.out.println(" │ Application │"); System.out.println(" └─────────────┘"); } }
|
哨兵机制的优势
- 高可用性:自动故障转移,减少服务中断时间
- 监控能力:实时监控Redis节点状态
- 配置管理:统一管理Redis配置信息
- 通知机制:及时通知系统管理员
- 客户端支持:提供客户端自动发现功能
哨兵工作原理
1. 监控机制
哨兵通过定期发送命令来监控Redis节点的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| public class SentinelMonitoring { private static final int MONITOR_INTERVAL = 1000; private static final int PING_TIMEOUT = 5000; public void monitorMaster(String masterName, String masterHost, int masterPort) { System.out.println("开始监控主节点: " + masterName + " " + masterHost + ":" + masterPort); ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { try { boolean isAlive = pingMaster(masterHost, masterPort); if (!isAlive) { System.out.println("主节点 " + masterName + " 无响应,可能故障"); handleMasterFailure(masterName); } else { System.out.println("主节点 " + masterName + " 正常"); } } catch (Exception e) { System.err.println("监控主节点失败: " + e.getMessage()); } }, 0, MONITOR_INTERVAL, TimeUnit.MILLISECONDS); } public void monitorSlaves(String masterName) { System.out.println("开始监控从节点: " + masterName); ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { try { List<SlaveInfo> slaves = getSlaveList(masterName); for (SlaveInfo slave : slaves) { boolean isAlive = pingSlave(slave.getHost(), slave.getPort()); if (!isAlive) { System.out.println("从节点 " + slave.getHost() + ":" + slave.getPort() + " 无响应"); handleSlaveFailure(slave); } } } catch (Exception e) { System.err.println("监控从节点失败: " + e.getMessage()); } }, 0, MONITOR_INTERVAL, TimeUnit.MILLISECONDS); } private boolean pingMaster(String host, int port) { try (Jedis jedis = new Jedis(host, port)) { String pong = jedis.ping(); return "PONG".equals(pong); } catch (Exception e) { return false; } } private boolean pingSlave(String host, int port) { try (Jedis jedis = new Jedis(host, port)) { String pong = jedis.ping(); return "PONG".equals(pong); } catch (Exception e) { return false; } } private void handleMasterFailure(String masterName) { System.out.println("处理主节点故障: " + masterName); } private void handleSlaveFailure(SlaveInfo slave) { System.out.println("处理从节点故障: " + slave.getHost() + ":" + slave.getPort()); } private List<SlaveInfo> getSlaveList(String masterName) { return new ArrayList<>(); } public static class SlaveInfo { private String host; private int port; public SlaveInfo(String host, int port) { this.host = host; this.port = port; } public String getHost() { return host; } public int getPort() { return port; } } }
|
2. 故障检测
哨兵使用主观下线和客观下线来判断节点是否故障:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class SentinelFailureDetection { private static final int DOWN_AFTER_MILLISECONDS = 30000; private static final int QUORUM = 2; private Map<String, Long> lastPingTime = new ConcurrentHashMap<>(); private Map<String, Integer> failureCount = new ConcurrentHashMap<>(); public boolean isSubjectivelyDown(String nodeId) { Long lastPing = lastPingTime.get(nodeId); if (lastPing == null) { return false; } long timeSinceLastPing = System.currentTimeMillis() - lastPing; return timeSinceLastPing > DOWN_AFTER_MILLISECONDS; } public boolean isObjectivelyDown(String masterName) { int sentinelCount = getSentinelCount(); int failureCount = getFailureCount(masterName); System.out.println("哨兵数量: " + sentinelCount + ", 故障计数: " + failureCount); return failureCount >= QUORUM; } public void updateNodeStatus(String nodeId, boolean isAlive) { if (isAlive) { lastPingTime.put(nodeId, System.currentTimeMillis()); failureCount.remove(nodeId); System.out.println("节点 " + nodeId + " 状态正常"); } else { int count = failureCount.getOrDefault(nodeId, 0) + 1; failureCount.put(nodeId, count); System.out.println("节点 " + nodeId + " 故障计数: " + count); } } public boolean shouldFailover(String masterName) { boolean subjectivelyDown = isSubjectivelyDown(masterName); boolean objectivelyDown = isObjectivelyDown(masterName); System.out.println("主节点 " + masterName + " 主观下线: " + subjectivelyDown); System.out.println("主节点 " + masterName + " 客观下线: " + objectivelyDown); return subjectivelyDown && objectivelyDown; } private int getSentinelCount() { return 3; } private int getFailureCount(String masterName) { return failureCount.getOrDefault(masterName, 0); } }
|
3. 故障转移流程
当检测到主节点故障时,哨兵会执行故障转移:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| public class SentinelFailover { public void executeFailover(String masterName) { System.out.println("开始执行故障转移: " + masterName); try { SlaveInfo newMaster = selectNewMaster(masterName); if (newMaster == null) { System.out.println("没有可用的从节点作为新主节点"); return; } promoteSlaveToMaster(newMaster); updateSlaveConfigurations(masterName, newMaster); updateSentinelConfigurations(masterName, newMaster); notifyClients(masterName, newMaster); System.out.println("故障转移完成,新主节点: " + newMaster.getHost() + ":" + newMaster.getPort()); } catch (Exception e) { System.err.println("故障转移失败: " + e.getMessage()); } } private SlaveInfo selectNewMaster(String masterName) { System.out.println("选择新的主节点..."); List<SlaveInfo> slaves = getAvailableSlaves(masterName); if (slaves.isEmpty()) { return null; } SlaveInfo selectedSlave = slaves.stream() .min(Comparator.comparing(SlaveInfo::getPriority)) .orElse(slaves.get(0)); System.out.println("选择的新主节点: " + selectedSlave.getHost() + ":" + selectedSlave.getPort()); return selectedSlave; } private void promoteSlaveToMaster(SlaveInfo slave) { System.out.println("提升从节点为主节点: " + slave.getHost() + ":" + slave.getPort()); try (Jedis jedis = new Jedis(slave.getHost(), slave.getPort())) { jedis.slaveofNoOne(); System.out.println("从节点已提升为主节点"); } catch (Exception e) { System.err.println("提升从节点失败: " + e.getMessage()); throw new RuntimeException("提升从节点失败", e); } } private void updateSlaveConfigurations(String masterName, SlaveInfo newMaster) { System.out.println("更新其他从节点配置..."); List<SlaveInfo> slaves = getAvailableSlaves(masterName); for (SlaveInfo slave : slaves) { if (!slave.equals(newMaster)) { try (Jedis jedis = new Jedis(slave.getHost(), slave.getPort())) { jedis.slaveof(newMaster.getHost(), newMaster.getPort()); System.out.println("从节点 " + slave.getHost() + ":" + slave.getPort() + " 已配置为跟随新主节点"); } catch (Exception e) { System.err.println("更新从节点配置失败: " + e.getMessage()); } } } } private void updateSentinelConfigurations(String masterName, SlaveInfo newMaster) { System.out.println("更新哨兵配置..."); List<SentinelInfo> sentinels = getSentinelList(); for (SentinelInfo sentinel : sentinels) { try (Jedis jedis = new Jedis(sentinel.getHost(), sentinel.getPort())) { jedis.sentinelSet(masterName, "host", newMaster.getHost()); jedis.sentinelSet(masterName, "port", String.valueOf(newMaster.getPort())); System.out.println("哨兵 " + sentinel.getHost() + ":" + sentinel.getPort() + " 配置已更新"); } catch (Exception e) { System.err.println("更新哨兵配置失败: " + e.getMessage()); } } } private void notifyClients(String masterName, SlaveInfo newMaster) { System.out.println("通知客户端主节点变更..."); try (Jedis jedis = new Jedis("localhost", 6379)) { String message = "MASTER_CHANGED:" + masterName + ":" + newMaster.getHost() + ":" + newMaster.getPort(); jedis.publish("sentinel:notifications", message); System.out.println("客户端通知已发送"); } catch (Exception e) { System.err.println("通知客户端失败: " + e.getMessage()); } } private List<SlaveInfo> getAvailableSlaves(String masterName) { return new ArrayList<>(); } private List<SentinelInfo> getSentinelList() { return new ArrayList<>(); } public static class SentinelInfo { private String host; private int port; public SentinelInfo(String host, int port) { this.host = host; this.port = port; } public String getHost() { return host; } public int getPort() { return port; } } }
|
哨兵配置
1. 哨兵配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel notification-script mymaster /path/to/notification.sh
sentinel client-reconfig-script mymaster /path/to/reconfig.sh
logfile /var/log/redis/sentinel.log loglevel notice
daemonize yes
pidfile /var/run/redis-sentinel.pid
|
2. Java中的哨兵配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| @Configuration public class SentinelRedisConfig { @Value("${redis.sentinel.master:mymaster}") private String masterName; @Value("${redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381}") private String sentinelNodes; @Value("${redis.sentinel.password:}") private String password; @Value("${redis.sentinel.timeout:2000}") private int timeout; @Bean public JedisConnectionFactory jedisConnectionFactory() { RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master(masterName); String[] nodes = sentinelNodes.split(","); for (String node : nodes) { String[] parts = node.trim().split(":"); sentinelConfig.sentinel(parts[0], Integer.parseInt(parts[1])); } if (!password.isEmpty()) { sentinelConfig.setPassword(password); } JedisConnectionFactory factory = new JedisConnectionFactory(sentinelConfig); factory.setTimeout(timeout); factory.afterPropertiesSet(); return factory; } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(jedisConnectionFactory()); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet(); return template; } @PostConstruct public void validateSentinelConfig() { System.out.println("哨兵配置验证:"); System.out.println("主节点名称: " + masterName); System.out.println("哨兵节点: " + sentinelNodes); System.out.println("连接超时: " + timeout + "ms"); } }
|
Java集成哨兵
1. 哨兵客户端实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| @Service public class SentinelClientService { @Autowired private RedisTemplate<String, Object> redisTemplate; public MasterInfo getCurrentMaster(String masterName) { try { Properties info = redisTemplate.getConnectionFactory() .getConnection() .info("replication"); String role = info.getProperty("role"); if ("master".equals(role)) { String host = info.getProperty("master_host"); String port = info.getProperty("master_port"); return new MasterInfo(host, Integer.parseInt(port)); } return null; } catch (Exception e) { System.err.println("获取主节点信息失败: " + e.getMessage()); return null; } } public List<SlaveInfo> getSlaveList(String masterName) { try { Properties info = redisTemplate.getConnectionFactory() .getConnection() .info("replication"); List<SlaveInfo> slaves = new ArrayList<>(); String slaveInfo = info.getProperty("slave0"); if (slaveInfo != null) { String[] parts = slaveInfo.split(","); for (String part : parts) { if (part.startsWith("ip=")) { String host = part.substring(3); String portPart = info.getProperty("slave0"); if (portPart != null && portPart.contains("port=")) { String portStr = portPart.substring(portPart.indexOf("port=") + 5); portStr = portStr.split(",")[0]; slaves.add(new SlaveInfo(host, Integer.parseInt(portStr))); } } } } return slaves; } catch (Exception e) { System.err.println("获取从节点列表失败: " + e.getMessage()); return new ArrayList<>(); } } public SentinelStatus checkSentinelStatus() { try { Properties info = redisTemplate.getConnectionFactory() .getConnection() .info("sentinel"); SentinelStatus status = new SentinelStatus(); status.setMasters(Integer.parseInt(info.getProperty("masters"))); status.setSlaves(Integer.parseInt(info.getProperty("slaves"))); status.setSentinels(Integer.parseInt(info.getProperty("sentinels"))); return status; } catch (Exception e) { System.err.println("检查哨兵状态失败: " + e.getMessage()); return null; } } public static class MasterInfo { private String host; private int port; public MasterInfo(String host, int port) { this.host = host; this.port = port; } public String getHost() { return host; } public int getPort() { return port; } } public static class SentinelStatus { private int masters; private int slaves; private int sentinels; public int getMasters() { return masters; } public void setMasters(int masters) { this.masters = masters; } public int getSlaves() { return slaves; } public void setSlaves(int slaves) { this.slaves = slaves; } public int getSentinels() { return sentinels; } public void setSentinels(int sentinels) { this.sentinels = sentinels; } } }
|
2. 哨兵监控服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| @Component public class SentinelMonitoringService { @Autowired private SentinelClientService sentinelClient; @Autowired private RedisTemplate<String, Object> redisTemplate; @Scheduled(fixedRate = 30000) public void monitorSentinelStatus() { try { System.out.println("=== 哨兵状态监控 ==="); SentinelClientService.SentinelStatus status = sentinelClient.checkSentinelStatus(); if (status != null) { System.out.println("主节点数量: " + status.getMasters()); System.out.println("从节点数量: " + status.getSlaves()); System.out.println("哨兵数量: " + status.getSentinels()); } SentinelClientService.MasterInfo master = sentinelClient.getCurrentMaster("mymaster"); if (master != null) { System.out.println("当前主节点: " + master.getHost() + ":" + master.getPort()); } else { System.out.println("主节点状态异常"); } List<SentinelClientService.SlaveInfo> slaves = sentinelClient.getSlaveList("mymaster"); System.out.println("从节点数量: " + slaves.size()); for (SentinelClientService.SlaveInfo slave : slaves) { System.out.println("从节点: " + slave.getHost() + ":" + slave.getPort()); } System.out.println("=== 监控结束 ===\n"); } catch (Exception e) { System.err.println("哨兵监控失败: " + e.getMessage()); } } public void manualFailover(String masterName) { try { System.out.println("手动触发故障转移: " + masterName); redisTemplate.getConnectionFactory() .getConnection() .sentinelFailover(masterName); System.out.println("故障转移已触发"); } catch (Exception e) { System.err.println("触发故障转移失败: " + e.getMessage()); } } public void resetSentinel(String masterName) { try { System.out.println("重置哨兵状态: " + masterName); redisTemplate.getConnectionFactory() .getConnection() .sentinelReset(masterName); System.out.println("哨兵状态已重置"); } catch (Exception e) { System.err.println("重置哨兵状态失败: " + e.getMessage()); } } }
|
哨兵最佳实践
1. 哨兵部署建议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Service public class SentinelDeploymentService { public void deploymentRecommendations() { System.out.println("哨兵部署建议:"); System.out.println("1. 哨兵数量: 建议至少3个哨兵节点"); System.out.println("2. 部署位置: 哨兵应部署在不同的物理机器上"); System.out.println("3. 网络配置: 确保哨兵之间网络连通性"); System.out.println("4. 法定人数: 设置合适的法定人数(quorum)"); System.out.println("5. 监控间隔: 合理设置监控间隔时间"); } public void configurationOptimization() { System.out.println("哨兵配置优化:"); System.out.println("1. down-after-milliseconds: 根据网络延迟调整"); System.out.println("2. failover-timeout: 设置合理的故障转移超时时间"); System.out.println("3. parallel-syncs: 控制并行同步的从节点数量"); System.out.println("4. notification-script: 配置通知脚本"); System.out.println("5. client-reconfig-script: 配置客户端重配置脚本"); } public void monitoringConfiguration() { System.out.println("哨兵监控配置:"); System.out.println("1. 日志级别: 设置合适的日志级别"); System.out.println("2. 日志文件: 配置日志文件路径"); System.out.println("3. 监控指标: 监控哨兵关键指标"); System.out.println("4. 告警机制: 配置告警通知机制"); System.out.println("5. 健康检查: 定期进行健康检查"); } }
|
2. 故障处理策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Service public class SentinelFailureHandlingService { public void failureHandlingStrategies() { System.out.println("故障处理策略:"); System.out.println("1. 预防措施: 定期备份、监控系统资源"); System.out.println("2. 检测机制: 多层次的故障检测"); System.out.println("3. 响应流程: 建立标准化的响应流程"); System.out.println("4. 恢复方案: 制定详细的恢复方案"); System.out.println("5. 测试验证: 定期进行故障转移测试"); } public void failoverTesting() { System.out.println("故障转移测试流程:"); System.out.println("1. 选择测试环境"); System.out.println("2. 模拟主节点故障"); System.out.println("3. 观察故障转移过程"); System.out.println("4. 验证新主节点功能"); System.out.println("5. 恢复原主节点"); System.out.println("6. 记录测试结果"); } public void performanceOptimization() { System.out.println("性能优化建议:"); System.out.println("1. 网络优化: 优化网络配置和延迟"); System.out.println("2. 资源监控: 监控CPU、内存、磁盘使用率"); System.out.println("3. 连接池: 合理配置连接池参数"); System.out.println("4. 缓存策略: 优化缓存策略"); System.out.println("5. 负载均衡: 合理分配负载"); } }
|
实际应用案例
1. 电商系统哨兵集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| @Service public class EcommerceSentinelService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private SentinelMonitoringService sentinelMonitoring; public void integrateEcommerceSentinel() { System.out.println("电商系统哨兵集成:"); System.out.println("1. 商品信息缓存: 使用哨兵确保高可用"); System.out.println("2. 用户会话管理: 通过哨兵实现会话持久化"); System.out.println("3. 购物车数据: 利用哨兵保证数据一致性"); System.out.println("4. 订单状态: 通过哨兵确保订单状态准确"); System.out.println("5. 库存管理: 使用哨兵保证库存数据可靠"); } public void cacheProductInfo(String productId, Object productInfo) { try { String key = "product:info:" + productId; redisTemplate.opsForValue().set(key, productInfo); redisTemplate.expire(key, Duration.ofHours(1)); System.out.println("商品信息缓存成功: " + productId); } catch (Exception e) { System.err.println("商品信息缓存失败: " + e.getMessage()); fallbackToDatabase(productId); } } public void manageUserSession(String userId, Object sessionData) { try { String key = "user:session:" + userId; redisTemplate.opsForValue().set(key, sessionData); redisTemplate.expire(key, Duration.ofMinutes(30)); System.out.println("用户会话管理成功: " + userId); } catch (Exception e) { System.err.println("用户会话管理失败: " + e.getMessage()); useLocalSession(userId, sessionData); } } public void manageCartData(String userId, Object cartData) { try { String key = "user:cart:" + userId; redisTemplate.opsForValue().set(key, cartData); System.out.println("购物车数据管理成功: " + userId); } catch (Exception e) { System.err.println("购物车数据管理失败: " + e.getMessage()); saveToDatabase(userId, cartData); } } private void fallbackToDatabase(String productId) { System.out.println("降级处理:从数据库查询商品信息: " + productId); } private void useLocalSession(String userId, Object sessionData) { System.out.println("降级处理:使用本地会话: " + userId); } private void saveToDatabase(String userId, Object cartData) { System.out.println("降级处理:保存到数据库: " + userId); } }
|
2. 金融系统哨兵应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| @Service public class FinancialSentinelService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private SentinelMonitoringService sentinelMonitoring; public void applyFinancialSentinel() { System.out.println("金融系统哨兵应用:"); System.out.println("1. 交易数据: 使用哨兵确保交易数据安全"); System.out.println("2. 账户余额: 通过哨兵保证余额数据准确"); System.out.println("3. 风控数据: 利用哨兵实现风控数据可靠"); System.out.println("4. 审计日志: 通过哨兵确保审计日志完整"); System.out.println("5. 配置信息: 使用哨兵管理配置信息"); } public void processTransactionData(String transactionId, Object transactionData) { try { String key = "transaction:" + transactionId; redisTemplate.opsForValue().set(key, transactionData); redisTemplate.expire(key, Duration.ofDays(7)); auditLog("TRANSACTION_PROCESS", transactionId, "SUCCESS"); System.out.println("交易数据处理成功: " + transactionId); } catch (Exception e) { auditLog("TRANSACTION_PROCESS", transactionId, "FAILED: " + e.getMessage()); System.err.println("交易数据处理失败: " + e.getMessage()); saveToDatabase(transactionId, transactionData); } } public void manageAccountBalance(String accountId, BigDecimal balance) { try { String key = "account:balance:" + accountId; redisTemplate.opsForValue().set(key, balance.toString()); redisTemplate.getConnectionFactory() .getConnection() .sync(); System.out.println("账户余额管理成功: " + accountId + " = " + balance); } catch (Exception e) { System.err.println("账户余额管理失败: " + e.getMessage()); updateDatabaseBalance(accountId, balance); } } public void processRiskControlData(String riskId, Object riskData) { try { String key = "risk:control:" + riskId; redisTemplate.opsForValue().set(key, riskData); redisTemplate.expire(key, Duration.ofHours(24)); System.out.println("风控数据处理成功: " + riskId); } catch (Exception e) { System.err.println("风控数据处理失败: " + e.getMessage()); useLocalCache(riskId, riskData); } } private void auditLog(String operation, String dataId, String result) { System.out.println("审计日志: " + operation + " | " + dataId + " | " + result); } private void saveToDatabase(String transactionId, Object transactionData) { System.out.println("降级处理:保存到数据库: " + transactionId); } private void updateDatabaseBalance(String accountId, BigDecimal balance) { System.out.println("降级处理:更新数据库余额: " + accountId + " = " + balance); } private void useLocalCache(String riskId, Object riskData) { System.out.println("降级处理:使用本地缓存: " + riskId); } }
|
总结
通过本文的详细介绍,我们深入了解了Redis的哨兵机制:
核心要点
- 哨兵架构:独立的监控进程,提供高可用性保障
- 监控机制:持续监控主从节点状态,及时发现问题
- 故障检测:主观下线和客观下线双重检测机制
- 故障转移:自动化的故障转移流程,最小化服务中断
- 配置管理:统一的配置管理和客户端发现功能
最佳实践
- 合理部署哨兵:至少3个哨兵节点,部署在不同机器上
- 优化配置参数:根据网络环境调整监控和超时参数
- 建立监控体系:完善的监控和告警机制
- 定期测试:定期进行故障转移测试
- 制定应急预案:建立标准化的故障处理流程
注意事项
- 哨兵本身也可能故障,需要部署多个哨兵节点
- 网络分区可能导致脑裂问题,需要合理设置法定人数
- 故障转移过程中会有短暂的服务不可用
- 需要定期备份和测试恢复流程
- 监控哨兵的性能和资源使用情况
掌握Redis哨兵机制的原理和实践,将大大提升系统的可用性和可靠性,为关键业务提供强有力的高可用保障。
在下一集中,我们将深入探讨Redis集群模式,了解如何实现水平扩展和负载均衡。