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
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 }
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 }
4. 总结
自己实现的一个小demo,废话比较少。小白自己写的配置类,理解有问题请留言!自己实现的方案感觉不妥,只是基本完成需求,还得继续研究。