Redis集群下过期key监听

1. 前言

在使用redis集群时,发现过期key始终监听不到。网上也没有现成的解决方案。于是想,既然不能监听集群,那我可以建立多个redis连接,分别对每个redis的key过期进行监听。以上做法可能不尽人意,目前也没找到好的解决方案,如果有好的想法,请留言告知哦!不多说,直接贴我自己的代码!

2. 代码实现

关于Redis集群配置代码此处不贴,直接贴配置监听类代码!

 1 redis.host1: 10.113.56.68
 2 redis.port1: 7030
 3 redis.host2: 10.113.56.68
 4 redis.port2: 7031
 5 redis.host3: 10.113.56.68
 6 redis.port3: 7032
 7 redis.host4: 10.113.56.68
 8 redis.port4: 7033
 9 redis.host5: 10.113.56.68
10 redis.port5: 7034
11 redis.host6: 10.113.56.68
12 redis.port6: 7035
application配置类
  1 import org.springframework.beans.factory.annotation.Value;
  2 import org.springframework.cache.CacheManager;
  3 import org.springframework.context.annotation.Bean;
  4 import org.springframework.context.annotation.Configuration;
  5 import org.springframework.data.redis.cache.RedisCacheManager;
  6 import org.springframework.data.redis.connection.RedisClusterConfiguration;
  7 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
  8 import org.springframework.data.redis.core.RedisTemplate;
  9 import org.springframework.data.redis.listener.RedisMessageListenerContainer;
 10 import org.springframework.data.redis.serializer.StringRedisSerializer;
 11 import redis.clients.jedis.Jedis;
 12 import redis.clients.jedis.JedisPoolConfig;
 13 
 14 import java.util.Arrays;
 15 
 16 /**
 17  * @Author  xiabing5
 18  * @Create  2019/8/6 14:46
 19  * @Desc    监听redis中Key过期事件
 20  **/
 21 @Configuration
 22 public class RedisListenerConfig {
 23 
 24     @Value("${redis.host1}")
 25     private String host1;
 26 
 27     @Value("${redis.host2}")
 28     private String host2;
 29 
 30     @Value("${redis.host3}")
 31     private String host3;
 32 
 33     @Value("${redis.host4}")
 34     private String host4;
 35 
 36     @Value("${redis.host5}")
 37     private String host5;
 38 
 39     @Value("${redis.host6}")
 40     private String host6;
 41 
 42     @Value("${redis.port1}")
 43     private int port1;
 44 
 45     @Value("${redis.port2}")
 46     private int port2;
 47 
 48     @Value("${redis.port3}")
 49     private int port3;
 50 
 51     @Value("${redis.port4}")
 52     private int port4;
 53 
 54     @Value("${redis.port5}")
 55     private int port5;
 56 
 57     @Value("${redis.port6}")
 58     private int port6;
 59 
 60     @Bean
 61     JedisPoolConfig jedisPoolConfig(){
 62         JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
 63         jedisPoolConfig.setMaxIdle(100);
 64         jedisPoolConfig.setMaxWaitMillis(1000);
 65         return jedisPoolConfig;
 66     }
 67 
 68     // redis-cluster不支持key过期监听,建立多个连接,对每个redis节点进行监听
 69     @Bean
 70     RedisMessageListenerContainer redisContainer1() {
 71         final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
 72         JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
 73         jedisConnectionFactory.setHostName(host1);
 74         jedisConnectionFactory.setPort(port1);
 75         jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
 76         jedisConnectionFactory.afterPropertiesSet();
 77         container.setConnectionFactory(jedisConnectionFactory);
 78         return container;
 79     }
 80 
 81     @Bean
 82     RedisMessageListenerContainer redisContainer2() {
 83         final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
 84         JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
 85         jedisConnectionFactory.setHostName(host2);
 86         jedisConnectionFactory.setPort(port2);
 87         jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
 88         jedisConnectionFactory.afterPropertiesSet();
 89         container.setConnectionFactory(jedisConnectionFactory);
 90         return container;
 91     }
 92 
 93     @Bean
 94     RedisMessageListenerContainer redisContainer3() {
 95         final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
 96         JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
 97         jedisConnectionFactory.setHostName(host3);
 98         jedisConnectionFactory.setPort(port3);
 99         jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
100         jedisConnectionFactory.afterPropertiesSet();
101         container.setConnectionFactory(jedisConnectionFactory);
102         return container;
103     }
104 
105     @Bean
106     RedisMessageListenerContainer redisContainer4() {
107         final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
108         JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
109         jedisConnectionFactory.setHostName(host4);
110         jedisConnectionFactory.setPort(port4);
111         jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
112         jedisConnectionFactory.afterPropertiesSet();
113         container.setConnectionFactory(jedisConnectionFactory);
114         return container;
115     }
116 
117     @Bean
118     RedisMessageListenerContainer redisContainer5() {
119         final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
120         JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
121         jedisConnectionFactory.setHostName(host5);
122         jedisConnectionFactory.setPort(port5);
123         jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
124         jedisConnectionFactory.afterPropertiesSet();
125         container.setConnectionFactory(jedisConnectionFactory);
126         return container;
127     }
128 
129     @Bean
130     RedisMessageListenerContainer redisContainer6() {
131         final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
132         JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
133         jedisConnectionFactory.setHostName(host6);
134         jedisConnectionFactory.setPort(port6);
135         jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
136         jedisConnectionFactory.afterPropertiesSet();
137         container.setConnectionFactory(jedisConnectionFactory);
138         return container;
139     }
140 
141     @Bean
142     RedisKeyExpirationListener redisKeyExpirationListener1() {
143         return new RedisKeyExpirationListener(redisContainer1());
144     }
145 
146     @Bean
147     RedisKeyExpirationListener redisKeyExpirationListener2() {
148         return new RedisKeyExpirationListener(redisContainer2());
149     }
150 
151     @Bean
152     RedisKeyExpirationListener redisKeyExpirationListener3() {
153         return new RedisKeyExpirationListener(redisContainer3());
154     }
155 
156     @Bean
157     RedisKeyExpirationListener redisKeyExpirationListener4() {
158         return new RedisKeyExpirationListener(redisContainer4());
159     }
160 
161     @Bean
162     RedisKeyExpirationListener redisKeyExpirationListener5() {
163         return new RedisKeyExpirationListener(redisContainer5());
164     }
165 
166     @Bean
167     RedisKeyExpirationListener redisKeyExpirationListener6() {
168         return new RedisKeyExpirationListener(redisContainer6());
169     }
170 
171 }
Bean配置类
 1 import org.springframework.beans.factory.annotation.Autowired;
 2 import org.springframework.data.redis.connection.Message;
 3 import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
 4 import org.springframework.data.redis.listener.RedisMessageListenerContainer;
 5 
 6 import java.util.Date;
 7 
 8 
 9 /**
10  * @Author  xiabing5
11  * @Create  2019/9/4 9:47
12  * @Desc    redis过期监听
13  **/
14 public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
15 
16     @Autowired
17     RedisUtil redisUtil;
18 
19     @Autowired
20     LoginUserStatisticsMapper loginUserStatisticsMapper;
21 
22     public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
23         super(listenerContainer);
24     }
25 
26     @Override
27     public void onMessage(Message message, byte[] pattern) {
28         // 用户做自己的业务处理即可,message.toString()可以获取失效的key
29         String mesg = message.toString();      
30        
31     }
32 }
监听操作类

3. Redis防止过期key重复监听

对于项目集群情况下,部署多个服务后,容易出现redis过期被多个服务同时监听到,从而执行相同的业务逻辑,这不是我们期望的。单机部署下方法的同步可以采用synchronize关键字。但集群下,就得采用分布式锁。在需要加锁的地方,只要加锁和解锁即可。此处正好写到Redis,那就贴一个自己用的redis分布式锁。

 1 import org.springframework.beans.factory.annotation.Autowired;
 2 import org.springframework.stereotype.Component;
 3 import redis.clients.jedis.Jedis;
 4 
 5 import java.util.Collections;
 6 import java.util.UUID;
 7 
 8 /**
 9  * @Author  xiabing5
10  * @Create  2019/9/6 15:54
11  * @Desc    redis分布式锁
12  **/
13 @Component
14 public class RedisLock {
15 
16     @Autowired
17     Jedis jedis;
18 
19     private static final String SET_IF_NOT_EXIST = "NX"; // NX表示如果不存在key就设置value
20     private static final String SET_WITH_EXPIRE_TIME = "PX"; // PX表示毫秒
21 
22     // 加锁
23     public String tryLock(String key,Long acquireTimeout) {
24         // 生成随机value
25         String identifierValue = UUID.randomUUID().toString();
26         // 设置超时时间
27         Long endTime = System.currentTimeMillis() + acquireTimeout;
28         // 循环获取锁
29         while (System.currentTimeMillis() < endTime) {
30             String result = jedis.set(key,identifierValue, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, acquireTimeout);
31             if("OK".equals(result)) {
32                 return identifierValue;
33             }
34         }
35         return null;
36     }
37 
38     // 解锁
39 //    public void delLock(String key,String identifierValue) {
40 //        // 判断是否是同一把锁
41 //        try{
42 //            if(jedis.get(key).equals(identifierValue)){
43 //                // 此处操作非原子性,容易造成释放非自己的锁
44 //                jedis.del(key);
45 //            }
46 //        }catch(Exception e) {
47 //            e.printStackTrace();
48 //        }
49 //    }
50 
51     // 使用Lua代码解锁
52     public void delLock(String key,String identifierValue) {
53         try{
54             String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
55             Long result = (Long) jedis.eval(script, Collections.singletonList(key), Collections.singletonList(identifierValue));
56             if (1 == result) {
57                System.out.println(result+"释放锁成功");
58             } if (0 == result) {
59                 System.out.println(result+"释放锁失败");
60             }
61         }catch (Exception e) {
62             e.printStackTrace();
63         }
64     }
65 
66 }
Redis锁解决分布式同步问题

4. 总结

自己实现的一个小demo,废话比较少。小白自己写的配置类,理解有问题请留言!自己实现的方案感觉不妥,只是基本完成需求,还得继续研究。

posted @ 2019-09-09 11:37  超人小冰  阅读(6350)  评论(0编辑  收藏  举报