1. Redis读写分离概述

Redis读写分离是一种高可用架构模式,通过将读操作和写操作分离到不同的Redis实例上,可以提高系统的并发处理能力和可用性。在副本数为2、实例规格为8GB的配置下,可以实现读写分离、负载均衡、故障转移等功能。本文将详细介绍Redis读写分离的原理、实现以及在Java企业级应用中的最佳实践。

1.1 Redis读写分离核心价值

  1. 性能提升: 读写分离提高系统并发处理能力
  2. 高可用性: 主从复制提供故障转移能力
  3. 负载均衡: 读操作分散到多个从节点
  4. 数据一致性: 保证数据的一致性和可靠性
  5. 扩展性: 支持水平扩展和垂直扩展
  6. 容错性: 单点故障不影响系统运行

1.2 Redis读写分离架构

  • 主节点(Master): 负责写操作和数据同步
  • 从节点(Slave): 负责读操作和数据备份
  • 哨兵(Sentinel): 负责监控和故障转移
  • 代理(Proxy): 负责请求路由和负载均衡

1.3 Redis读写分离模式

  • 主从复制: 一主多从的复制模式
  • 哨兵模式: 自动故障转移的监控模式
  • 集群模式: 分布式集群的扩展模式
  • 代理模式: 通过代理实现读写分离

2. Redis读写分离配置

2.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
96
97
98
99
100
101
102
/**
* Redis读写分离配置类
* @author Java实战
*/
@Configuration
@EnableConfigurationProperties(RedisReadWriteProperties.class)
public class RedisReadWriteConfig {

@Autowired
private RedisReadWriteProperties properties;

/**
* Redis主节点连接工厂
* @return 主节点连接工厂
*/
@Bean
@Primary
public LettuceConnectionFactory masterConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(properties.getMaster().getHost());
config.setPort(properties.getMaster().getPort());
config.setPassword(properties.getMaster().getPassword());
config.setDatabase(properties.getMaster().getDatabase());

LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
factory.setTimeout(Duration.ofMillis(properties.getMaster().getTimeout()));
factory.setShareNativeConnection(properties.getMaster().isShareNativeConnection());

return factory;
}

/**
* Redis从节点连接工厂
* @return 从节点连接工厂
*/
@Bean
public LettuceConnectionFactory slaveConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(properties.getSlave().getHost());
config.setPort(properties.getSlave().getPort());
config.setPassword(properties.getSlave().getPassword());
config.setDatabase(properties.getSlave().getDatabase());

LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
factory.setTimeout(Duration.ofMillis(properties.getSlave().getTimeout()));
factory.setShareNativeConnection(properties.getSlave().isShareNativeConnection());

return factory;
}

/**
* Redis主节点模板
* @return 主节点模板
*/
@Bean
@Primary
public RedisTemplate<String, Object> masterRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(masterConnectionFactory());

// 序列化配置
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}

/**
* Redis从节点模板
* @return 从节点模板
*/
@Bean
public RedisTemplate<String, Object> slaveRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(slaveConnectionFactory());

// 序列化配置
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}

/**
* Redis读写分离服务
* @return 读写分离服务
*/
@Bean
public RedisReadWriteService redisReadWriteService() {
return new RedisReadWriteService();
}

private static final Logger logger = LoggerFactory.getLogger(RedisReadWriteConfig.class);
}

2.2 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
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/**
* Redis读写分离属性配置
* @author Java实战
*/
@Data
@ConfigurationProperties(prefix = "redis.readwrite")
public class RedisReadWriteProperties {

/**
* 主节点配置
*/
private MasterConfig master = new MasterConfig();

/**
* 从节点配置
*/
private SlaveConfig slave = new SlaveConfig();

/**
* 哨兵配置
*/
private SentinelConfig sentinel = new SentinelConfig();

/**
* 集群配置
*/
private ClusterConfig cluster = new ClusterConfig();

/**
* 性能配置
*/
private PerformanceConfig performance = new PerformanceConfig();

/**
* 主节点配置类
*/
@Data
public static class MasterConfig {
/**
* 主节点主机
*/
private String host = "localhost";

/**
* 主节点端口
*/
private int port = 6379;

/**
* 主节点密码
*/
private String password = "";

/**
* 主节点数据库
*/
private int database = 0;

/**
* 主节点超时时间(毫秒)
*/
private int timeout = 2000;

/**
* 是否共享原生连接
*/
private boolean shareNativeConnection = true;

/**
* 最大连接数
*/
private int maxConnections = 100;

/**
* 最小空闲连接数
*/
private int minIdleConnections = 10;

/**
* 连接超时时间(毫秒)
*/
private int connectionTimeout = 2000;

/**
* 读取超时时间(毫秒)
*/
private int readTimeout = 2000;
}

/**
* 从节点配置类
*/
@Data
public static class SlaveConfig {
/**
* 从节点主机
*/
private String host = "localhost";

/**
* 从节点端口
*/
private int port = 6380;

/**
* 从节点密码
*/
private String password = "";

/**
* 从节点数据库
*/
private int database = 0;

/**
* 从节点超时时间(毫秒)
*/
private int timeout = 2000;

/**
* 是否共享原生连接
*/
private boolean shareNativeConnection = true;

/**
* 最大连接数
*/
private int maxConnections = 100;

/**
* 最小空闲连接数
*/
private int minIdleConnections = 10;

/**
* 连接超时时间(毫秒)
*/
private int connectionTimeout = 2000;

/**
* 读取超时时间(毫秒)
*/
private int readTimeout = 2000;

/**
* 从节点列表
*/
private List<SlaveNode> nodes = new ArrayList<>();

/**
* 从节点信息
*/
@Data
public static class SlaveNode {
private String host;
private int port;
private String password;
private int database;
private int timeout;
private boolean enabled = true;
}
}

/**
* 哨兵配置类
*/
@Data
public static class SentinelConfig {
/**
* 是否启用哨兵
*/
private boolean enable = false;

/**
* 哨兵主机列表
*/
private List<String> hosts = new ArrayList<>();

/**
* 哨兵端口
*/
private int port = 26379;

/**
* 主节点名称
*/
private String masterName = "mymaster";

/**
* 哨兵密码
*/
private String password = "";

/**
* 哨兵超时时间(毫秒)
*/
private int timeout = 2000;

/**
* 哨兵数据库
*/
private int database = 0;
}

/**
* 集群配置类
*/
@Data
public static class ClusterConfig {
/**
* 是否启用集群
*/
private boolean enable = false;

/**
* 集群节点列表
*/
private List<String> nodes = new ArrayList<>();

/**
* 集群密码
*/
private String password = "";

/**
* 集群超时时间(毫秒)
*/
private int timeout = 2000;

/**
* 最大重定向次数
*/
private int maxRedirects = 3;

/**
* 集群数据库
*/
private int database = 0;
}

/**
* 性能配置类
*/
@Data
public static class PerformanceConfig {
/**
* 是否启用读写分离
*/
private boolean enableReadWriteSeparation = true;

/**
* 读操作超时时间(毫秒)
*/
private int readTimeout = 1000;

/**
* 写操作超时时间(毫秒)
*/
private int writeTimeout = 2000;

/**
* 连接池大小
*/
private int poolSize = 10;

/**
* 最大连接数
*/
private int maxConnections = 100;

/**
* 最小空闲连接数
*/
private int minIdleConnections = 5;

/**
* 连接超时时间(毫秒)
*/
private int connectionTimeout = 2000;

/**
* 是否启用压缩
*/
private boolean enableCompression = false;

/**
* 压缩阈值(字节)
*/
private int compressionThreshold = 1024;
}
}

2.3 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
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/**
* Redis读写分离服务
* @author Java实战
*/
@Service
@Slf4j
public class RedisReadWriteService {

@Autowired
@Qualifier("masterRedisTemplate")
private RedisTemplate<String, Object> masterRedisTemplate;

@Autowired
@Qualifier("slaveRedisTemplate")
private RedisTemplate<String, Object> slaveRedisTemplate;

@Autowired
private RedisReadWriteProperties properties;

@Autowired
private RedisReadWriteMonitorService monitorService;

/**
* 写入数据(主节点)
* @param key 键
* @param value 值
* @param ttl 过期时间(秒)
*/
public void write(String key, Object value, int ttl) {
log.info("写入数据,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("键不能为空");
}

if (value == null) {
throw new IllegalArgumentException("值不能为空");
}

long startTime = System.currentTimeMillis();

// 写入主节点
if (ttl > 0) {
masterRedisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
} else {
masterRedisTemplate.opsForValue().set(key, value);
}

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordWrite(key, duration, true);

log.info("写入数据成功,键: {}, 耗时: {}ms", key, duration);

} catch (Exception e) {
log.error("写入数据异常,键: {}", key, e);

// 记录监控信息
monitorService.recordWrite(key, 0, false);

throw new RuntimeException("写入数据失败: " + e.getMessage());
}
}

/**
* 写入数据(主节点,无过期时间)
* @param key 键
* @param value 值
*/
public void write(String key, Object value) {
write(key, value, 0);
}

/**
* 读取数据(从节点)
* @param key 键
* @return
*/
public Object read(String key) {
log.info("读取数据,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("键不能为空");
}

long startTime = System.currentTimeMillis();

// 从从节点读取
Object value = slaveRedisTemplate.opsForValue().get(key);

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordRead(key, duration, true);

if (value != null) {
log.info("读取数据成功,键: {}, 耗时: {}ms", key, duration);
} else {
log.warn("数据不存在,键: {}", key);
}

return value;

} catch (Exception e) {
log.error("读取数据异常,键: {}", key, e);

// 记录监控信息
monitorService.recordRead(key, 0, false);

// 如果从节点读取失败,尝试从主节点读取
try {
log.info("从节点读取失败,尝试从主节点读取,键: {}", key);
return masterRedisTemplate.opsForValue().get(key);
} catch (Exception ex) {
log.error("从主节点读取也失败,键: {}", key, ex);
return null;
}
}
}

/**
* 读取数据(指定类型)
* @param key 键
* @param clazz 类型
* @return
*/
@SuppressWarnings("unchecked")
public <T> T read(String key, Class<T> clazz) {
Object value = read(key);
if (value != null && clazz.isAssignableFrom(value.getClass())) {
return (T) value;
}
return null;
}

/**
* 删除数据(主节点)
* @param key 键
* @return 是否成功
*/
public boolean delete(String key) {
log.info("删除数据,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("键不能为空");
}

long startTime = System.currentTimeMillis();

// 从主节点删除
boolean result = Boolean.TRUE.equals(masterRedisTemplate.delete(key));

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordDelete(key, duration, result);

log.info("删除数据结果,键: {}, 结果: {}, 耗时: {}ms", key, result, duration);

return result;

} catch (Exception e) {
log.error("删除数据异常,键: {}", key, e);

// 记录监控信息
monitorService.recordDelete(key, 0, false);

return false;
}
}

/**
* 检查键是否存在(从节点)
* @param key 键
* @return 是否存在
*/
public boolean exists(String key) {
log.info("检查键是否存在,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("键不能为空");
}

long startTime = System.currentTimeMillis();

// 从从节点检查
boolean result = Boolean.TRUE.equals(slaveRedisTemplate.hasKey(key));

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordExists(key, duration, result);

log.info("检查键是否存在结果,键: {}, 结果: {}, 耗时: {}ms", key, result, duration);

return result;

} catch (Exception e) {
log.error("检查键是否存在异常,键: {}", key, e);

// 记录监控信息
monitorService.recordExists(key, 0, false);

// 如果从节点检查失败,尝试从主节点检查
try {
log.info("从节点检查失败,尝试从主节点检查,键: {}", key);
return Boolean.TRUE.equals(masterRedisTemplate.hasKey(key));
} catch (Exception ex) {
log.error("从主节点检查也失败,键: {}", key, ex);
return false;
}
}
}

/**
* 设置过期时间(主节点)
* @param key 键
* @param ttl 过期时间(秒)
* @return 是否成功
*/
public boolean expire(String key, int ttl) {
log.info("设置过期时间,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("键不能为空");
}

long startTime = System.currentTimeMillis();

// 在主节点设置过期时间
boolean result = Boolean.TRUE.equals(masterRedisTemplate.expire(key, Duration.ofSeconds(ttl)));

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordExpire(key, duration, result);

log.info("设置过期时间结果,键: {}, 结果: {}, 耗时: {}ms", key, result, duration);

return result;

} catch (Exception e) {
log.error("设置过期时间异常,键: {}", key, e);

// 记录监控信息
monitorService.recordExpire(key, 0, false);

return false;
}
}

/**
* 获取过期时间(从节点)
* @param key 键
* @return 过期时间(秒)
*/
public long getExpire(String key) {
log.info("获取过期时间,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("键不能为空");
}

long startTime = System.currentTimeMillis();

// 从从节点获取过期时间
Long expire = slaveRedisTemplate.getExpire(key);

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordGetExpire(key, duration, expire != null);

long result = expire != null ? expire : -1;

log.info("获取过期时间结果,键: {}, 过期时间: {}s, 耗时: {}ms", key, result, duration);

return result;

} catch (Exception e) {
log.error("获取过期时间异常,键: {}", key, e);

// 记录监控信息
monitorService.recordGetExpire(key, 0, false);

// 如果从节点获取失败,尝试从主节点获取
try {
log.info("从节点获取失败,尝试从主节点获取,键: {}", key);
Long expire = masterRedisTemplate.getExpire(key);
return expire != null ? expire : -1;
} catch (Exception ex) {
log.error("从主节点获取也失败,键: {}", key, ex);
return -1;
}
}
}

/**
* 批量写入数据(主节点)
* @param data 数据Map
* @param ttl 过期时间(秒)
*/
public void batchWrite(Map<String, Object> data, int ttl) {
log.info("批量写入数据,数量: {}, TTL: {}", data.size(), ttl);

try {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("数据不能为空");
}

long startTime = System.currentTimeMillis();

// 批量写入主节点
if (ttl > 0) {
for (Map.Entry<String, Object> entry : data.entrySet()) {
masterRedisTemplate.opsForValue().set(entry.getKey(), entry.getValue(), Duration.ofSeconds(ttl));
}
} else {
for (Map.Entry<String, Object> entry : data.entrySet()) {
masterRedisTemplate.opsForValue().set(entry.getKey(), entry.getValue());
}
}

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordBatchWrite(data.size(), duration, true);

log.info("批量写入数据成功,数量: {}, 耗时: {}ms", data.size(), duration);

} catch (Exception e) {
log.error("批量写入数据异常,数量: {}", data.size(), e);

// 记录监控信息
monitorService.recordBatchWrite(data.size(), 0, false);

throw new RuntimeException("批量写入数据失败: " + e.getMessage());
}
}

/**
* 批量读取数据(从节点)
* @param keys 键列表
* @return 数据Map
*/
public Map<String, Object> batchRead(List<String> keys) {
log.info("批量读取数据,数量: {}", keys.size());

try {
if (keys == null || keys.isEmpty()) {
throw new IllegalArgumentException("键列表不能为空");
}

long startTime = System.currentTimeMillis();

// 批量从从节点读取
Map<String, Object> result = new HashMap<>();
for (String key : keys) {
Object value = slaveRedisTemplate.opsForValue().get(key);
if (value != null) {
result.put(key, value);
}
}

long endTime = System.currentTimeMillis();
long duration = endTime - startTime;

// 记录监控信息
monitorService.recordBatchRead(keys.size(), duration, true);

log.info("批量读取数据成功,请求数量: {}, 实际数量: {}, 耗时: {}ms",
keys.size(), result.size(), duration);

return result;

} catch (Exception e) {
log.error("批量读取数据异常,数量: {}", keys.size(), e);

// 记录监控信息
monitorService.recordBatchRead(keys.size(), 0, false);

// 如果从节点读取失败,尝试从主节点读取
try {
log.info("从节点批量读取失败,尝试从主节点读取,数量: {}", keys.size());
Map<String, Object> result = new HashMap<>();
for (String key : keys) {
Object value = masterRedisTemplate.opsForValue().get(key);
if (value != null) {
result.put(key, value);
}
}
return result;
} catch (Exception ex) {
log.error("从主节点批量读取也失败,数量: {}", keys.size(), ex);
return new HashMap<>();
}
}
}

/**
* 获取Redis状态
* @return Redis状态
*/
public RedisStatus getRedisStatus() {
log.info("获取Redis状态");

try {
RedisStatus status = new RedisStatus();

// 获取主节点状态
RedisNodeStatus masterStatus = getNodeStatus(masterRedisTemplate, "master");
status.setMasterStatus(masterStatus);

// 获取从节点状态
RedisNodeStatus slaveStatus = getNodeStatus(slaveRedisTemplate, "slave");
status.setSlaveStatus(slaveStatus);

// 设置总体状态
if (masterStatus.isHealthy() && slaveStatus.isHealthy()) {
status.setOverallStatus("HEALTHY");
} else if (masterStatus.isHealthy() || slaveStatus.isHealthy()) {
status.setOverallStatus("DEGRADED");
} else {
status.setOverallStatus("DOWN");
}

status.setLastCheckTime(LocalDateTime.now());

log.info("获取Redis状态成功,总体状态: {}", status.getOverallStatus());

return status;

} catch (Exception e) {
log.error("获取Redis状态异常", e);
return null;
}
}

/**
* 获取节点状态
* @param template Redis模板
* @param nodeType 节点类型
* @return 节点状态
*/
private RedisNodeStatus getNodeStatus(RedisTemplate<String, Object> template, String nodeType) {
RedisNodeStatus status = new RedisNodeStatus();
status.setNodeType(nodeType);

try {
// 测试连接
template.opsForValue().get("test");

status.setHealthy(true);
status.setLastCheckTime(LocalDateTime.now());

} catch (Exception e) {
status.setHealthy(false);
status.setLastCheckTime(LocalDateTime.now());
status.setErrorMessage(e.getMessage());
}

return status;
}
}

3. Redis主从复制实现

3.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
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/**
* Redis主从复制服务
* @author Java实战
*/
@Service
@Slf4j
public class RedisReplicationService {

@Autowired
private RedisReadWriteProperties properties;

@Autowired
private RedisReadWriteMonitorService monitorService;

private final Map<String, RedisTemplate<String, Object>> slaveTemplates = new ConcurrentHashMap<>();

/**
* 初始化主从复制
*/
@PostConstruct
public void initReplication() {
log.info("初始化Redis主从复制");

try {
// 初始化从节点连接
initSlaveConnections();

// 启动复制监控
startReplicationMonitoring();

log.info("Redis主从复制初始化成功");

} catch (Exception e) {
log.error("Redis主从复制初始化失败", e);
}
}

/**
* 初始化从节点连接
*/
private void initSlaveConnections() {
log.info("初始化从节点连接");

try {
List<RedisReadWriteProperties.SlaveConfig.SlaveNode> nodes = properties.getSlave().getNodes();

for (RedisReadWriteProperties.SlaveConfig.SlaveNode node : nodes) {
if (node.isEnabled()) {
RedisTemplate<String, Object> template = createSlaveTemplate(node);
String nodeKey = node.getHost() + ":" + node.getPort();
slaveTemplates.put(nodeKey, template);

log.info("从节点连接初始化成功,节点: {}", nodeKey);
}
}

} catch (Exception e) {
log.error("初始化从节点连接异常", e);
}
}

/**
* 创建从节点模板
* @param node 从节点配置
* @return 从节点模板
*/
private RedisTemplate<String, Object> createSlaveTemplate(RedisReadWriteProperties.SlaveConfig.SlaveNode node) {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(node.getHost());
config.setPort(node.getPort());
config.setPassword(node.getPassword());
config.setDatabase(node.getDatabase());

LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
factory.setTimeout(Duration.ofMillis(node.getTimeout()));

RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// 序列化配置
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}

/**
* 启动复制监控
*/
private void startReplicationMonitoring() {
log.info("启动复制监控");

try {
// 这里可以添加复制监控的逻辑
// 例如:监控复制延迟、复制状态等

log.info("复制监控启动成功");

} catch (Exception e) {
log.error("启动复制监控异常", e);
}
}

/**
* 获取复制状态
* @return 复制状态
*/
public ReplicationStatus getReplicationStatus() {
log.info("获取复制状态");

try {
ReplicationStatus status = new ReplicationStatus();

// 获取主节点信息
status.setMasterHost(properties.getMaster().getHost());
status.setMasterPort(properties.getMaster().getPort());

// 获取从节点信息
List<SlaveNodeStatus> slaveStatuses = new ArrayList<>();
for (Map.Entry<String, RedisTemplate<String, Object>> entry : slaveTemplates.entrySet()) {
SlaveNodeStatus slaveStatus = getSlaveNodeStatus(entry.getKey(), entry.getValue());
slaveStatuses.add(slaveStatus);
}
status.setSlaveStatuses(slaveStatuses);

// 计算总体状态
boolean allHealthy = slaveStatuses.stream().allMatch(SlaveNodeStatus::isHealthy);
status.setOverallHealthy(allHealthy);

status.setLastCheckTime(LocalDateTime.now());

log.info("获取复制状态成功,从节点数量: {}, 总体健康: {}",
slaveStatuses.size(), allHealthy);

return status;

} catch (Exception e) {
log.error("获取复制状态异常", e);
return null;
}
}

/**
* 获取从节点状态
* @param nodeKey 节点键
* @param template 节点模板
* @return 从节点状态
*/
private SlaveNodeStatus getSlaveNodeStatus(String nodeKey, RedisTemplate<String, Object> template) {
SlaveNodeStatus status = new SlaveNodeStatus();
status.setNodeKey(nodeKey);

try {
// 测试连接
template.opsForValue().get("test");

status.setHealthy(true);
status.setLastCheckTime(LocalDateTime.now());

} catch (Exception e) {
status.setHealthy(false);
status.setLastCheckTime(LocalDateTime.now());
status.setErrorMessage(e.getMessage());
}

return status;
}

/**
* 检查复制延迟
* @return 复制延迟信息
*/
public ReplicationLag getReplicationLag() {
log.info("检查复制延迟");

try {
ReplicationLag lag = new ReplicationLag();

// 这里可以添加检查复制延迟的逻辑
// 例如:比较主从节点的偏移量差异

lag.setLastCheckTime(LocalDateTime.now());

log.info("检查复制延迟完成");

return lag;

} catch (Exception e) {
log.error("检查复制延迟异常", e);
return null;
}
}

/**
* 手动触发复制
* @return 是否成功
*/
public boolean triggerReplication() {
log.info("手动触发复制");

try {
// 这里可以添加手动触发复制的逻辑
// 例如:发送复制命令到从节点

log.info("手动触发复制成功");

return true;

} catch (Exception e) {
log.error("手动触发复制异常", e);
return false;
}
}
}

3.2 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
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/**
* Redis哨兵模式服务
* @author Java实战
*/
@Service
@Slf4j
public class RedisSentinelService {

@Autowired
private RedisReadWriteProperties properties;

@Autowired
private RedisReadWriteMonitorService monitorService;

private final Map<String, RedisTemplate<String, Object>> sentinelTemplates = new ConcurrentHashMap<>();

/**
* 初始化哨兵模式
*/
@PostConstruct
public void initSentinel() {
log.info("初始化Redis哨兵模式");

try {
if (properties.getSentinel().isEnable()) {
// 初始化哨兵连接
initSentinelConnections();

// 启动哨兵监控
startSentinelMonitoring();

log.info("Redis哨兵模式初始化成功");
} else {
log.info("Redis哨兵模式未启用");
}

} catch (Exception e) {
log.error("Redis哨兵模式初始化失败", e);
}
}

/**
* 初始化哨兵连接
*/
private void initSentinelConnections() {
log.info("初始化哨兵连接");

try {
List<String> hosts = properties.getSentinel().getHosts();

for (String host : hosts) {
RedisTemplate<String, Object> template = createSentinelTemplate(host);
sentinelTemplates.put(host, template);

log.info("哨兵连接初始化成功,主机: {}", host);
}

} catch (Exception e) {
log.error("初始化哨兵连接异常", e);
}
}

/**
* 创建哨兵模板
* @param host 哨兵主机
* @return 哨兵模板
*/
private RedisTemplate<String, Object> createSentinelTemplate(String host) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
config.master(properties.getSentinel().getMasterName());
config.sentinel(host, properties.getSentinel().getPort());
config.setPassword(properties.getSentinel().getPassword());
config.setDatabase(properties.getSentinel().getDatabase());

LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
factory.setTimeout(Duration.ofMillis(properties.getSentinel().getTimeout()));

RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// 序列化配置
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}

/**
* 启动哨兵监控
*/
private void startSentinelMonitoring() {
log.info("启动哨兵监控");

try {
// 这里可以添加哨兵监控的逻辑
// 例如:监控主节点状态、从节点状态等

log.info("哨兵监控启动成功");

} catch (Exception e) {
log.error("启动哨兵监控异常", e);
}
}

/**
* 获取哨兵状态
* @return 哨兵状态
*/
public SentinelStatus getSentinelStatus() {
log.info("获取哨兵状态");

try {
SentinelStatus status = new SentinelStatus();

// 获取哨兵配置
status.setMasterName(properties.getSentinel().getMasterName());
status.setHosts(properties.getSentinel().getHosts());
status.setPort(properties.getSentinel().getPort());

// 获取哨兵节点状态
List<SentinelNodeStatus> nodeStatuses = new ArrayList<>();
for (Map.Entry<String, RedisTemplate<String, Object>> entry : sentinelTemplates.entrySet()) {
SentinelNodeStatus nodeStatus = getSentinelNodeStatus(entry.getKey(), entry.getValue());
nodeStatuses.add(nodeStatus);
}
status.setNodeStatuses(nodeStatuses);

// 计算总体状态
boolean allHealthy = nodeStatuses.stream().allMatch(SentinelNodeStatus::isHealthy);
status.setOverallHealthy(allHealthy);

status.setLastCheckTime(LocalDateTime.now());

log.info("获取哨兵状态成功,节点数量: {}, 总体健康: {}",
nodeStatuses.size(), allHealthy);

return status;

} catch (Exception e) {
log.error("获取哨兵状态异常", e);
return null;
}
}

/**
* 获取哨兵节点状态
* @param host 哨兵主机
* @param template 哨兵模板
* @return 哨兵节点状态
*/
private SentinelNodeStatus getSentinelNodeStatus(String host, RedisTemplate<String, Object> template) {
SentinelNodeStatus status = new SentinelNodeStatus();
status.setHost(host);

try {
// 测试连接
template.opsForValue().get("test");

status.setHealthy(true);
status.setLastCheckTime(LocalDateTime.now());

} catch (Exception e) {
status.setHealthy(false);
status.setLastCheckTime(LocalDateTime.now());
status.setErrorMessage(e.getMessage());
}

return status;
}

/**
* 检查主节点状态
* @return 主节点状态
*/
public MasterNodeStatus checkMasterStatus() {
log.info("检查主节点状态");

try {
MasterNodeStatus status = new MasterNodeStatus();

// 这里可以添加检查主节点状态的逻辑
// 例如:检查主节点是否可访问、是否为主节点等

status.setLastCheckTime(LocalDateTime.now());

log.info("检查主节点状态完成");

return status;

} catch (Exception e) {
log.error("检查主节点状态异常", e);
return null;
}
}

/**
* 执行故障转移
* @return 是否成功
*/
public boolean executeFailover() {
log.info("执行故障转移");

try {
// 这里可以添加执行故障转移的逻辑
// 例如:选择新的主节点、更新配置等

log.info("执行故障转移成功");

return true;

} catch (Exception e) {
log.error("执行故障转移异常", e);
return false;
}
}
}

4. Redis监控管理

4.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
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/**
* Redis监控服务
* @author Java实战
*/
@Service
@Slf4j
public class RedisReadWriteMonitorService {

private final Map<String, OperationMetrics> operationMetrics = new ConcurrentHashMap<>();

/**
* 记录写操作
* @param key 键
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordWrite(String key, long duration, boolean success) {
log.debug("记录写操作,键: {}, 耗时: {}ms, 成功: {}", key, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("write", k -> new OperationMetrics("write"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录写操作异常", e);
}
}

/**
* 记录读操作
* @param key 键
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordRead(String key, long duration, boolean success) {
log.debug("记录读操作,键: {}, 耗时: {}ms, 成功: {}", key, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("read", k -> new OperationMetrics("read"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录读操作异常", e);
}
}

/**
* 记录删除操作
* @param key 键
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordDelete(String key, long duration, boolean success) {
log.debug("记录删除操作,键: {}, 耗时: {}ms, 成功: {}", key, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("delete", k -> new OperationMetrics("delete"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录删除操作异常", e);
}
}

/**
* 记录存在检查操作
* @param key 键
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordExists(String key, long duration, boolean success) {
log.debug("记录存在检查操作,键: {}, 耗时: {}ms, 成功: {}", key, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("exists", k -> new OperationMetrics("exists"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录存在检查操作异常", e);
}
}

/**
* 记录过期时间设置操作
* @param key 键
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordExpire(String key, long duration, boolean success) {
log.debug("记录过期时间设置操作,键: {}, 耗时: {}ms, 成功: {}", key, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("expire", k -> new OperationMetrics("expire"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录过期时间设置操作异常", e);
}
}

/**
* 记录过期时间获取操作
* @param key 键
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordGetExpire(String key, long duration, boolean success) {
log.debug("记录过期时间获取操作,键: {}, 耗时: {}ms, 成功: {}", key, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("getExpire", k -> new OperationMetrics("getExpire"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录过期时间获取操作异常", e);
}
}

/**
* 记录批量写操作
* @param count 数量
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordBatchWrite(int count, long duration, boolean success) {
log.debug("记录批量写操作,数量: {}, 耗时: {}ms, 成功: {}", count, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("batchWrite", k -> new OperationMetrics("batchWrite"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录批量写操作异常", e);
}
}

/**
* 记录批量读操作
* @param count 数量
* @param duration 耗时(毫秒)
* @param success 是否成功
*/
public void recordBatchRead(int count, long duration, boolean success) {
log.debug("记录批量读操作,数量: {}, 耗时: {}ms, 成功: {}", count, duration, success);

try {
OperationMetrics metrics = operationMetrics.computeIfAbsent("batchRead", k -> new OperationMetrics("batchRead"));

metrics.incrementTotalCount();
if (success) {
metrics.incrementSuccessCount();
} else {
metrics.incrementFailureCount();
}
metrics.addDuration(duration);
metrics.setLastOperationTime(LocalDateTime.now());

} catch (Exception e) {
log.error("记录批量读操作异常", e);
}
}

/**
* 获取操作指标
* @param operationType 操作类型
* @return 操作指标
*/
public OperationMetrics getOperationMetrics(String operationType) {
log.info("获取操作指标,操作类型: {}", operationType);

try {
OperationMetrics metrics = operationMetrics.get(operationType);

if (metrics != null) {
log.info("获取操作指标成功,操作类型: {}, 总次数: {}, 成功次数: {}, 失败次数: {}",
operationType, metrics.getTotalCount(), metrics.getSuccessCount(), metrics.getFailureCount());
} else {
log.warn("操作指标不存在,操作类型: {}", operationType);
}

return metrics;

} catch (Exception e) {
log.error("获取操作指标异常,操作类型: {}", operationType, e);
return null;
}
}

/**
* 获取所有操作指标
* @return 操作指标Map
*/
public Map<String, OperationMetrics> getAllOperationMetrics() {
log.info("获取所有操作指标");

try {
Map<String, OperationMetrics> result = new HashMap<>(operationMetrics);

log.info("获取所有操作指标成功,操作类型数量: {}", result.size());

return result;

} catch (Exception e) {
log.error("获取所有操作指标异常", e);
return new HashMap<>();
}
}

/**
* 重置操作指标
*/
public void resetOperationMetrics() {
log.info("重置操作指标");

try {
operationMetrics.clear();

log.info("重置操作指标成功");

} catch (Exception e) {
log.error("重置操作指标异常", e);
}
}
}

4.2 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
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* Redis监控控制器
* @author Java实战
*/
@RestController
@RequestMapping("/api/redis")
@Slf4j
public class RedisMonitorController {

@Autowired
private RedisReadWriteService redisReadWriteService;

@Autowired
private RedisReplicationService replicationService;

@Autowired
private RedisSentinelService sentinelService;

@Autowired
private RedisReadWriteMonitorService monitorService;

/**
* 获取Redis状态
* @return Redis状态
*/
@GetMapping("/status")
public ResponseEntity<RedisStatus> getRedisStatus() {
try {
log.info("接收到获取Redis状态请求");

RedisStatus status = redisReadWriteService.getRedisStatus();

if (status != null) {
return ResponseEntity.ok(status);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

} catch (Exception e) {
log.error("获取Redis状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取复制状态
* @return 复制状态
*/
@GetMapping("/replication/status")
public ResponseEntity<ReplicationStatus> getReplicationStatus() {
try {
log.info("接收到获取复制状态请求");

ReplicationStatus status = replicationService.getReplicationStatus();

if (status != null) {
return ResponseEntity.ok(status);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

} catch (Exception e) {
log.error("获取复制状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取哨兵状态
* @return 哨兵状态
*/
@GetMapping("/sentinel/status")
public ResponseEntity<SentinelStatus> getSentinelStatus() {
try {
log.info("接收到获取哨兵状态请求");

SentinelStatus status = sentinelService.getSentinelStatus();

if (status != null) {
return ResponseEntity.ok(status);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

} catch (Exception e) {
log.error("获取哨兵状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取操作指标
* @param operationType 操作类型
* @return 操作指标
*/
@GetMapping("/metrics/{operationType}")
public ResponseEntity<OperationMetrics> getOperationMetrics(@PathVariable String operationType) {
try {
log.info("接收到获取操作指标请求,操作类型: {}", operationType);

OperationMetrics metrics = monitorService.getOperationMetrics(operationType);

if (metrics != null) {
return ResponseEntity.ok(metrics);
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}

} catch (Exception e) {
log.error("获取操作指标失败,操作类型: {}", operationType, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取所有操作指标
* @return 操作指标Map
*/
@GetMapping("/metrics")
public ResponseEntity<Map<String, OperationMetrics>> getAllOperationMetrics() {
try {
log.info("接收到获取所有操作指标请求");

Map<String, OperationMetrics> metrics = monitorService.getAllOperationMetrics();

return ResponseEntity.ok(metrics);

} catch (Exception e) {
log.error("获取所有操作指标失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 重置操作指标
* @return 是否成功
*/
@PostMapping("/metrics/reset")
public ResponseEntity<Boolean> resetOperationMetrics() {
try {
log.info("接收到重置操作指标请求");

monitorService.resetOperationMetrics();

return ResponseEntity.ok(true);

} catch (Exception e) {
log.error("重置操作指标失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 健康检查
* @return 健康状态
*/
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
try {
log.info("接收到Redis健康检查请求");

Map<String, Object> health = new HashMap<>();

// 检查Redis状态
RedisStatus redisStatus = redisReadWriteService.getRedisStatus();
boolean redisHealthy = redisStatus != null && "HEALTHY".equals(redisStatus.getOverallStatus());

// 检查复制状态
ReplicationStatus replicationStatus = replicationService.getReplicationStatus();
boolean replicationHealthy = replicationStatus != null && replicationStatus.isOverallHealthy();

// 检查哨兵状态
SentinelStatus sentinelStatus = sentinelService.getSentinelStatus();
boolean sentinelHealthy = sentinelStatus != null && sentinelStatus.isOverallHealthy();

boolean overallHealthy = redisHealthy && replicationHealthy && sentinelHealthy;

health.put("status", overallHealthy ? "UP" : "DOWN");
health.put("timestamp", System.currentTimeMillis());
health.put("redisStatus", redisStatus);
health.put("replicationStatus", replicationStatus);
health.put("sentinelStatus", sentinelStatus);

return ResponseEntity.ok(health);

} catch (Exception e) {
log.error("Redis健康检查失败", e);

Map<String, Object> health = new HashMap<>();
health.put("status", "DOWN");
health.put("timestamp", System.currentTimeMillis());
health.put("error", e.getMessage());

return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(health);
}
}
}

5. 总结

5.1 Redis读写分离总结

  1. 性能提升: 读写分离提高系统并发处理能力
  2. 高可用性: 主从复制提供故障转移能力
  3. 负载均衡: 读操作分散到多个从节点
  4. 数据一致性: 保证数据的一致性和可靠性
  5. 扩展性: 支持水平扩展和垂直扩展
  6. 容错性: 单点故障不影响系统运行

5.2 Redis读写分离架构

  • 主节点(Master): 负责写操作和数据同步
  • 从节点(Slave): 负责读操作和数据备份
  • 哨兵(Sentinel): 负责监控和故障转移
  • 代理(Proxy): 负责请求路由和负载均衡

5.3 最佳实践建议

  • 合理配置参数: 根据业务场景合理配置Redis参数
  • 监控系统状态: 实时监控Redis系统状态
  • 优化读写策略: 优化读写分离策略
  • 故障处理: 完善故障处理和恢复机制
  • 性能调优: 根据监控数据优化性能
  • 容量规划: 合理规划Redis容量

通过本文的Redis读写分离Java实战指南,您可以掌握Redis读写分离的原理、实现、监控管理以及在企业级应用中的最佳实践,构建高效、稳定、可扩展的Redis系统!