SpringBoot 2.0 连接 Redis 主从、哨兵集群

  |     |  

前景

上一篇文章中我使用docker-compose搭建了redis的主从复制,并启动3个哨兵容器相互监控。

https://blog.csdn.net/qq_39211866/article/details/88044546

现在,我要使用SpringBoot来连接redis集群,
由于使用了哨兵模式,显然,不能向之前配置单节点那样配置连接池了,
节点经过故障转移后,主从结构已经发生了改变且主已经死亡,如果还按照之前那样写死IP的方式连接Redis的话,势必会出现错误。
可以想到,在Sentinel结构下,你必须向哨兵询问来获取谁是Master。

更改后的自定义哨兵模式连接池与配置如下:
由于是用docker搭建的redis主从、哨兵集群,所以访问哨兵在推举master时,给的master ip 为内网地址,所以需要将项目一同编排进
docker-compose.yml中,方可使用。

1
2
3
4
5
6
7
8
9
10
java.redis.pool.max-total=10
java.redis.pool.max-idle=5
java.redis.pool.min-idle=5
# 多个用逗号隔开,host:port,host2:port2
java.redis.sentinel.nodes=sentinel1:26379,sentinel2:26380,sentinel3:26381
java.redis.sentinel.password=123456
java.redis.sentinel.master-name=mymaster
java.redis.sentinel.pool.max-total=10
java.redis.sentinel.pool.max-idle=5
java.redis.sentinel.pool.min-idle=5

连接池使用JedisSentinelPool。

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
@Slf4j
public class RedisSentinelPool {

@Value("${java.redis.pool.max-total:10}")
private Integer redisMaxTotal;
@Value("${java.redis.pool.min-idle}")
private Integer redisMaxIdle;
@Value("${java.redis.pool.min-idle}")
private Integer redisMinIdle;
@Value("${java.redis.sentinel.nodes}")
private String redisSentinelNodes;
@Value("${java.redis.sentinel.password}")
private String redisSentinelPassword;
@Value("${java.redis.sentinel.master-name}")
private String redisSentinelMasterName;
@Value("${java.redis.sentinel.pool.max-total}")
private Integer redisSentinelMaxTotal;
@Value("${java.redis.sentinel.pool.max-idle}")
private Integer redisSentinelMaxIdle;
@Value("${java.redis.sentinel.pool.min-idle}")
private Integer redisSentinelMinIdle;

private JedisSentinelPool jedisPool;

public void init() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(redisMaxTotal);
jedisPoolConfig.setMaxIdle(redisMaxIdle);
jedisPoolConfig.setMinIdle(redisMinIdle);
String[] hosts = redisSentinelNodes.split(",");
Set<String> sentinels = new HashSet<>(Arrays.asList(hosts));
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(redisSentinelMaxTotal);
poolConfig.setMaxIdle(redisSentinelMaxIdle);
poolConfig.setMinIdle(redisSentinelMinIdle);
jedisPool = new JedisSentinelPool(redisSentinelMasterName, sentinels, jedisPoolConfig, redisSentinelPassword);
if (isConnectSuccess()) {
// 初始化成功
log.info("++++++ Init Redis Sentinel Pool SUCCESS! ++++++");
} else {
// 初始化失败
log.info("++++++ Init Redis Sentinel Pool FAILURE! ++++++");
}
}

/**
* Del long.
*
* @param key the key
* @return the long
*/
public long del(String key) {
Jedis jedis = getRedis();
long result = jedis.del(key);
recycleRedis(jedis);
return result;
}

/**
* 获取当前key的生成时长
*
* @param key the key
* @return the long
*/
public long ttl(String key) {
Jedis jedis = getRedis();
long result = jedis.ttl(key);
recycleRedis(jedis);
return result;
}

/**
* Get string.
*
* @param key the key
* @return the string
*/
public String get(String key) {
Jedis jedis = getRedis();
String result = jedis.get(key);
recycleRedis(jedis);
return result;
}

/**
* Sets .
*
* @param key the key
* @param value the value
* @param expireSeconds the expire seconds
* @return the
*/
public String setex(String key, String value, int expireSeconds) {
Jedis jedis = getRedis();
String result = jedis.setex(key, expireSeconds, value);
recycleRedis(jedis);
return result;
}

/**
* 重新初始化连接池
*/
public void reload() {
close();
init();
}

/**
* 销毁Redis连接池
*/
public void close() {
if (null != jedisPool) {
jedisPool.close();
}
}

/**
* 资源回收
*
* @param jedis jedis实例
*/
private void recycleRedis(Jedis jedis) {
jedis.close();
}

/**
* @return 连接池中的Jedis实例
*/
private Jedis getRedis() {
return jedisPool.getResource();
}

/**
* @return 是否成功连接上redis
*/
private boolean isConnectSuccess() {
Jedis jedis = getRedis();
String ping = jedis.ping();
recycleRedis(jedis);
return ping.equals("PONG");
}
}

配置完后,添加两个测试接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class TestRedisApi {

@Autowired
RedisSentinelPool redisSentinelPool;

@RequestMapping("/setKey")
public String setKey(String key, String value) {
return redisSentinelPool.setex(key, value, 60);
}

@RequestMapping("/getKey")
public String getKey(String key) {
return redisSentinelPool.get(key);
}
}

使用gradle构建Docker镜像,测试项目子模块过多,建议构建时,删剩当前的子模块即可,在SpringBoot-Learning目录下构建:

1
2
gradle clean build -x test
gradle SpringBoot-Redis-Sentinel:docker

接着更改哨兵集群的docker-compose.yml,加入该测试镜像的资源编排:

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
version: '3'
services:
sentinel1:
image: redis ## 镜像
container_name: redis-sentinel-1
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
ports:
- "26379:26379" ## 暴露端口
volumes:
- "./sentinel1.conf:/usr/local/etc/redis/sentinel.conf"
sentinel2:
image: redis ## 镜像
container_name: redis-sentinel-2
ports:
- "26380:26379" ## 暴露端口
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- "./sentinel2.conf:/usr/local/etc/redis/sentinel.conf"
sentinel3:
image: redis ## 镜像
container_name: redis-sentinel-3
ports:
- "26381:26379" ## 暴露端口
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf
java-demo:
image: redis-docker/SpringBoot-redis-sentinel:1.0.0
ports:
- "8080:8080"
depends_on:
- sentinel1
- sentinel2
- sentinel3
networks:
default:
external:
name: redis_sentinel-master

重启启动下哨兵集群即可。
测试:


可以看到,成功接入redis集群。

学习本就逆流而上,不进则退。

源码已上传GitHub:https://github.com/liaozihong/SpringBoot-Learning/tree/master/SpringBoot-Redis-Sentinel

文章目录
  1. 1. 前景
作者共写了53.1k个字 本站总访问量  |   您是访问本站的第个小伙伴