当前位置: 首页 > Spring Boot, 缓存 > 正文

基于Spring 的 Redis Sentinel 读写分离 Slave 连接池

目 录
 [ 隐藏 ]

0. 背景 

Reids除了配置集群实现高可用之外,对于单机版的Redis,可以通过Master-Slave架构,配合使用Sentinel机制实现高可用架构, 
同时客户端可以实现自动失效转移。 

类似于JdbcTemplate,Spring中使用RedisTemplate来操作Redis。Spring Boot中只需引入如下Maven依赖,即可自动配置 
一个RedisTemplate实例。

RedisTemplate需要一个RedisConnectionFactory来管理Redis连接。 可以在项目中定义一个RedisSentinelConfiguration给 
RedisConnectionFactory,即可生成一个基于Sentinel的连接池,并且实现了自动失效转移:当master失效时,Sentinel自动提升一个slave 
成为master保证Redis的master连接高可用。 

下面是基于Sentinel的RedisConnectionFactory的典型配置 

查看 org.springframework.data.redis.connection.jedis.JedisConnectionFactory源码发现, 
当配置了RedisSentinelConfiguration后,RedisConnectionFactory会返回一个JedisSentinelPool连接池。该连接池里面所有的连接 
都是连接到Master上面的。 同时,在JedisSentinelPool中为每一个Sentinel都配置了+switch-master频道的监听。 当监听到+switch-master消息后 
表示发生了master切换,有新的Master产生,然后会重新初始化到新Master的连接池。 

至此,我们知道基于Sentinel可以创建RedisConnectionFactory,并可实现自动失效转移, 
但RedisConnectionFactory只会创建到Master的连接。 一般情况下,如果所有的连接都是连接到Master上面,Slave就完全当成Master的备份了,造成性能浪费。 
通常,Slave只是单纯的复制Master的数据,为避免数据不一致,不应该往Slave写数据,可以在Redis配置文件中配置slave-read-only yes,让Slave拒绝所有的写操作。 
于是,对于一个基于Sentinel的Master-Slave Redis 服务器来说,可以将Master配置为可读写服务器,将所有Slave配置为只读服务器来实现读写分离,以充分利用服务器资源, 
并提高整个Redis系统的性能。 

1. 提出问题 

JedisSentinelPool连接池中的连接都是到Master的连接,那么如何获取到Slave的连接池呢? 分析了spring-boot-starter-data-redis和jedis之后,发现, 
并没有现成的Slave连接池可以拿来用,于是决定写一个。 

2. 分析问题 

通过RedisSentinelConfiguration,可以拿到sentinel的IP和端口,就可以连接到sentinel,再调用sentinel slaves mymaster命令,就可以拿到slave的IP和port。 
然后就可以创建到slave的连接了。 

继续查看JedisFactory源码,了解到其实现了PooledObjectFactory<Jedis>接口,该接口来自org.apache.commons.pool2,由此可见,Jedis连接池是借助Apache 
commons.pool2来实现的。 

由图看到,JedisConnectionFactory创建一个JedisSentinelPool,JedisSentinelPool创建JedisFactory,JedisFactory实现了PooledObjectFactory接口 
,在MakeObject()方法中产生新的Redis连接。 在JedisSentinelPool中定义MasterListener还订阅+switch-master频道,一旦发生Master转移事件,自动作失效转移 
重新初始化master连接池。 

3. 解决问题 

模仿JedisConnectionFactory,JedisSentinelPool,和JedisFactory, 创建JedisSentinelSlaveConnectionFactory,JedisSentinelSlavePool和JedisSentinelSlaveFactory 
它们之间的关系,如图UML-2所示。 

其中,JedisSentinelSlaveConnectionFactory就是可以传递给RedisTemplate的。JedisSentinelSlaveConnectionFactory继承自JedisConnectionFactory 
并且覆盖了createRedisSentinelPool方法,在JedisConnectionFactory中,该方法会返回一个JedisSentinelPool,而新的方法会返回JedisSentinelSlavePool。 
JedisSentinelSlavePool和JedisSentinelPool都是继承自Pool<Jedis>的。 JedisSentinelSlavePool会生成JedisSentinelSlaveFactory, 
JedisSentinelSlaveFactory实现了PooledObjectFactory<Jedis>接口,在public PooledObject<Jedis> makeObject()方法中,通过sentinel连接, 
调用sentinel slaves命令,获取所有可用的slave的ip和port,然后随机的创建一个slave连接并返回。 

JedisSentinelSlaveConnectionFactory的createRedisSentinelPool方法 

1) 通过配置RedisSentinelConfiguration传递sentinel配置和master name给JedisSentinelSlaveConnectionFactory,然后sentinel配置和master name 
    会传递到JedisSentinelSlavePool和JedisSentinelSlaveFactory中 
2)创建 JedisSentinelSlavePool,在JedisSentinelSlavePool中启动监听,监听”+switch-master”频道,一旦新master产生,即初始化连接池 
3) 连接池有JedisSentinelSlaveFactory来代理,JedisSentinelSlaveFactory实现了PooledObjectFactory<Jedis> 
在makeObject()中首先根据配置的Sentinel Set找到一个可用的sentinel连接,然后执行sentinel slaves master_name获取所有slave列表 
随机选择一个slave创建连接。 如果连接不成功则重试,最大重试5次,依然不能成功创建连接则抛出异常。 
4) 由图uml-2可知,JedisConnectionFactory实现了InitializingBean,Spring会在Bean初始化之后,调用接口方法void afterPropertiesSet() throws Exception; 
在这个方法中创建连接池 
5) JedisConnectionFactory实现了DisposableBean,会在Spring 容器销毁时,调用public void destroy() 方法销毁连接池

4 实战 

4.1  redis-sentinel-slave-connection-factory 工程结构 

1) pom文件 

2) JedisSentinelSlaveFactory.java 

3) JedisSentinelSlavePool.java 

4) JedisSentinelSlaveConnectionFactory.java 

4.2 测试

在应用中,只需配置如下的JedisSentinelSlaveConnectionFactory,Spring Boot会自动配置一个 
RedisTemplate<String,String> redisTemplate和StringRedisTemplate stringRedisTemplate; 
在代码中使用@Autowired注入即可。

1) pom.xml 

2) RedisConfiguration.java 

3) RedisDemoApplication.java 

4) DemoApplicationTests.java 

 

5. 总结 

优点: 
连接池中的连接是随机建立的到所有slave的连接 
当监测到master失效转移会自动初始化连接池,确保不会连接到master上 
新增slave时可以自动被发现 
slave下线会被自动侦测到,然后重新初始化连接池,确保不会连接到已经下线的slave 
缺点: 
reids slave 需要设置slave-read-only yes 
slave同步master数据需要时间,在一个短暂时间内master和slave数据会不一致 

打个赏呗

   微信打赏  支付宝打赏


本文固定链接: https://www.jack-yin.com/coding/spring-boot/2683.html | 边城网事

该日志由 边城网事 于2018年05月29日发表在 Spring Boot, 缓存 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 基于Spring 的 Redis Sentinel 读写分离 Slave 连接池 | 边城网事

基于Spring 的 Redis Sentinel 读写分离 Slave 连接池:目前有3 条留言

  1. 沙发
    jikeou:

    JedisSentinelSlavePool 根本就没有去获取slave的信息

    2018-07-20 13:53 [回复]
    • 边城网事:

      连接池有JedisSentinelSlaveFactory来代理,JedisSentinelSlaveFactory实现了PooledObjectFactory
      在makeObject()中首先根据配置的Sentinel Set找到一个可用的sentinel连接,然后执行sentinel slaves master_name获取所有slave列表
      随机选择一个slave创建连接。

      2018-07-21 13:21 [回复]
    • 边城网事:

      看下 JedisSentinelSlaveFactory.makeObject()方法就明白了

      2018-07-21 13:30 [回复]

发表评论

快捷键:Ctrl+Enter