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

  • 相关阅读:
    Kubernetes日志的6个最佳实践
    如何选出适合自己的管理Helm Chart的最佳方式?
    授权权限服务设计解析
    微服务中如何设计一个权限授权服务
    微服务中的网关
    ketchup服务治理
    ketchup 消息队列rabbitmq使用
    ketchup 注册中心consul使用
    微服务框架 ketchup 介绍
    微服务框架surging学习之路——序列化
  • 原文地址:https://www.cnblogs.com/xiaobingblog/p/11490726.html
Copyright © 2020-2023  润新知