Jedis 如何支持 Sentinel
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jedis 如何支持 Sentinel相关的知识,希望对你有一定的参考价值。
参考技术A Jedis 作为 Java 世界 Redis 的老牌客户端,很好的支持了 Sentinel,例如 Sentinel 的故障转移功能。Jedis 提供了一个 Sentinel 构造方法:
该构造方法做了 2件事情,初始化 Sentinel 和 Pool。我们先看看 initSentinel。
方法很长,简单说说逻辑:
看看这个线程的主要内容:
该方法已经写了很多注释,稍微说下逻辑:根据哨兵的 host 和 port 创建一个 jedis 对象,然后,这个 jedis 对象订阅了 pub/sub 消息,,消息的主题是 "+switch-master" ,如果收到消息了,就执行 onMessage 方法,该方法会根据新的 master 信息重新初始化 Redis 连接池。
那么如何初始化连接池的呢?
事实上,在 Sentinel 构造器里面,也会调用这个方法,第一次调用的时候, factory 肯定是 null,第二次调用的时候,会设置 factory 的 hostAndPort 为新的 master 地址,然后清空原来的连接池。那么新的 getResource 方法就会从这个新的地址获取到新的连接了。
具体关于 JedisSentinelPool 的 getResource 方法就不细说了,大家可以自己看看,还是很简单的,
可以看到 Sentinel 的通知客户端机制,是需要客户端进行配合的,客户端需要通过 Sentinel 的 pub/sub 机制订阅哨兵节点的 +switch-master 主题,当 master 改变的时候,会通过 pub 通知客户端,客户端此时就可以优雅的更新连接池。
Spring+Redis+Sentinel
master:192.168.25.129:6379
slave:192.168.25.129:6380,192.168.25.130:6379,192.168.25.130:6380
jar包:jedis-2.5.2,spring-data-redis-1.4.1.RELEASE
spring配置文件:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> 9 <bean id="propertyConfigurer" 10 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 11 <property name="locations"> 12 <list> 13 <value>classpath:redis.properties</value> 14 </list> 15 </property> 16 </bean> 17 <bean id="redisSentinelConfiguration" 18 class="org.springframework.data.redis.connection.RedisSentinelConfiguration"> 19 <property name="master"> 20 <bean class="org.springframework.data.redis.connection.RedisNode"> 21 <property name="name" value="mymaster"></property> 22 </bean> 23 </property> 24 <property name="sentinels"> 25 <set> 26 <bean class="org.springframework.data.redis.connection.RedisNode"> 27 <constructor-arg index="0" value="${redis1.ip}" /> 28 <constructor-arg index="1" value="${redis1.port}" /> 29 </bean> 30 <bean class="org.springframework.data.redis.connection.RedisNode"> 31 <constructor-arg index="0" value="${redis2.ip}" /> 32 <constructor-arg index="1" value="${redis2.port}" /> 33 </bean> 34 </set> 35 </property> 36 </bean> 37 38 <bean id="jedisConnFactory" 39 class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> 40 <property name="hostName" value="192.168.25.129"/> 41 <property name="port" value="6379"/> 42 <property name="usePool" value="false"/> 43 <constructor-arg ref="redisSentinelConfiguration" /> 44 </bean> 45 46 <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> 47 <property name="connectionFactory" ref="jedisConnFactory" /> 48 </bean> 49 </beans>
redis.properties:
1 redis1.ip=192.168.25.129 2 redis1.port=26379 3 4 redis2.ip=192.168.25.130 5 redis2.port=26379
测试代码:
1 public class RedisTest { 2 3 public static void main(String[] args) { 4 ConfigurableApplicationContext ctx = null; 5 try { 6 ctx = new ClassPathXmlApplicationContext("applicationContext_redis_sentinel.xml"); 7 final StringRedisTemplate redisTemplate = ctx.getBean(StringRedisTemplate.class); 8 9 new Thread(new Runnable() { 10 @Override 11 public void run() { 12 int loopNum = 60; 13 while (loopNum-->0) { 14 redisTemplate.opsForValue().set("win_time",new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date())); 15 System.out.println(redisTemplate.opsForValue().get("win_time")); 16 try { 17 Thread.sleep(1000); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 } 22 } 23 }).start(); 24 25 // 写入bean 26 JedisConnectionFactory jedisConnectionFactory = ctx.getBean(JedisConnectionFactory.class); 27 ObjectRedisTemplate userRedisTemplate = new ObjectRedisTemplate(jedisConnectionFactory,User.class); 28 User user = new User(); 29 user.setId(1); 30 user.setName("test"); 31 userRedisTemplate.opsForValue().set("user1",user); 32 System.out.println(userRedisTemplate.opsForValue().get("user1")); 33 } catch(Exception e) { 34 e.printStackTrace(); 35 } finally { 36 if (ctx != null && ctx.isActive()) { 37 ctx.close(); 38 } 39 } 40 41 } 42 43 static class User implements Serializable { 44 private int id; 45 private String name; 46 47 public int getId() { 48 return id; 49 } 50 51 public void setId(int id) { 52 this.id = id; 53 } 54 55 public String getName() { 56 return name; 57 } 58 59 public void setName(String name) { 60 this.name = name; 61 } 62 63 @Override 64 public String toString() { 65 return "User{" + 66 "id=" + id + 67 ", name=‘" + name + ‘\‘‘ + 68 ‘}‘; 69 } 70 } 71 }
有几个问题:
1)JedisConnecitonFactory的配置中必须指定hostName和Port,否则就使用的默认值localhost和6379;
2)使用redis-cli -p 6379 shutdown使master宕掉后,程序执行出错,没法连接redis;
3)即便2没有问题,自动切换到了新的master,因为1中指定了master的ip和port,那么程序重启就连不上redis。
以上是关于Jedis 如何支持 Sentinel的主要内容,如果未能解决你的问题,请参考以下文章