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>
View Code

 

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
View Code

 

测试代码:

技术分享
 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 }
View Code

 

有几个问题:

1)JedisConnecitonFactory的配置中必须指定hostName和Port,否则就使用的默认值localhost和6379;

2)使用redis-cli -p 6379 shutdown使master宕掉后,程序执行出错,没法连接redis;

3)即便2没有问题,自动切换到了新的master,因为1中指定了master的ip和port,那么程序重启就连不上redis。

以上是关于Jedis 如何支持 Sentinel的主要内容,如果未能解决你的问题,请参考以下文章

Jedis那么低性能,还在用?赶紧换上 lettuce 吧

Redis的客户端Jedis

SpringBoot 操作 Redis的各种实现(Jedis、Redisson的区别比较)

Redis入门很简单之六Jedis常见操作

Jedis与Redisson对比有什么优缺点?

(二)连接Redis,Lettuce与Jedis客户端