引言

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
// Redis哨兵架构示例
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; // 1秒
private static final int PING_TIMEOUT = 5000; // 5秒

// 监控主节点
public void monitorMaster(String masterName, String masterHost, int masterPort) {
System.out.println("开始监控主节点: " + masterName + " " + masterHost + ":" + masterPort);

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
try {
// 发送PING命令
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; // 30秒
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; // 假设有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 {
// 1. 选择新的主节点
SlaveInfo newMaster = selectNewMaster(masterName);
if (newMaster == null) {
System.out.println("没有可用的从节点作为新主节点");
return;
}

// 2. 提升从节点为主节点
promoteSlaveToMaster(newMaster);

// 3. 更新其他从节点配置
updateSlaveConfigurations(masterName, newMaster);

// 4. 更新哨兵配置
updateSentinelConfigurations(masterName, newMaster);

// 5. 通知客户端
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())) {
// 执行 SLAVEOF NO ONE 命令
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
# sentinel.conf 哨兵配置文件
# 端口配置
port 26379

# 监控的主节点配置
# sentinel monitor <master-name> <ip> <port> <quorum>
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

# PID文件
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) // 每30秒执行一次
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);

// 通过哨兵API触发故障转移
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的哨兵机制:

核心要点

  1. 哨兵架构:独立的监控进程,提供高可用性保障
  2. 监控机制:持续监控主从节点状态,及时发现问题
  3. 故障检测:主观下线和客观下线双重检测机制
  4. 故障转移:自动化的故障转移流程,最小化服务中断
  5. 配置管理:统一的配置管理和客户端发现功能

最佳实践

  • 合理部署哨兵:至少3个哨兵节点,部署在不同机器上
  • 优化配置参数:根据网络环境调整监控和超时参数
  • 建立监控体系:完善的监控和告警机制
  • 定期测试:定期进行故障转移测试
  • 制定应急预案:建立标准化的故障处理流程

注意事项

  • 哨兵本身也可能故障,需要部署多个哨兵节点
  • 网络分区可能导致脑裂问题,需要合理设置法定人数
  • 故障转移过程中会有短暂的服务不可用
  • 需要定期备份和测试恢复流程
  • 监控哨兵的性能和资源使用情况

掌握Redis哨兵机制的原理和实践,将大大提升系统的可用性和可靠性,为关键业务提供强有力的高可用保障。

在下一集中,我们将深入探讨Redis集群模式,了解如何实现水平扩展和负载均衡。