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,廢話比較少。小白自己寫的配置類,理解有問題請留言!自己實現的方案感覺不妥,只是基本完成需求,還得繼續研究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM