1. 程式人生 > 資料庫 >Redis叢集下過期key監聽的實現程式碼

Redis叢集下過期key監聽的實現程式碼

1. 前言

在使用redis叢集時,發現過期key始終監聽不到。網上也沒有現成的解決方案。於是想,既然不能監聽叢集,那我可以建立多個redis連線,分別對每個redis的key過期進行監聽。以上做法可能不盡人意,目前也沒找到好的解決方案,如果有好的想法,請留言告知哦!不多說,直接貼我自己的程式碼!

2. 程式碼實現

關於Redis叢集配置程式碼此處不貼,直接貼配置監聽類程式碼!

redis.host1: 10.113.56.68
redis.port1: 7030
redis.host2: 10.113.56.68
redis.port2: 7031
redis.host3: 10.113.56.68
redis.port3: 7032
redis.host4: 10.113.56.68
redis.port4: 7033
redis.host5: 10.113.56.68
redis.port5: 7034
redis.host6: 10.113.56.68
redis.port6: 7035
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Arrays;

/**
 * @Author xiabing5
 * @Create 2019/8/6 14:46
 * @Desc  監聽redis中Key過期事件
 **/
@Configuration
public class RedisListenerConfig {

  @Value("${redis.host1}")
  private String host1;

  @Value("${redis.host2}")
  private String host2;

  @Value("${redis.host3}")
  private String host3;

  @Value("${redis.host4}")
  private String host4;

  @Value("${redis.host5}")
  private String host5;

  @Value("${redis.host6}")
  private String host6;

  @Value("${redis.port1}")
  private int port1;

  @Value("${redis.port2}")
  private int port2;

  @Value("${redis.port3}")
  private int port3;

  @Value("${redis.port4}")
  private int port4;

  @Value("${redis.port5}")
  private int port5;

  @Value("${redis.port6}")
  private int port6;

  @Bean
  JedisPoolConfig jedisPoolConfig(){
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxIdle(100);
    jedisPoolConfig.setMaxWaitMillis(1000);
    return jedisPoolConfig;
  }

  // redis-cluster不支援key過期監聽,建立多個連線,對每個redis節點進行監聽
  @Bean
  RedisMessageListenerContainer redisContainer1() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName(host1);
    jedisConnectionFactory.setPort(port1);
    jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
    jedisConnectionFactory.afterPropertiesSet();
    container.setConnectionFactory(jedisConnectionFactory);
    return container;
  }

  @Bean
  RedisMessageListenerContainer redisContainer2() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName(host2);
    jedisConnectionFactory.setPort(port2);
    jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
    jedisConnectionFactory.afterPropertiesSet();
    container.setConnectionFactory(jedisConnectionFactory);
    return container;
  }

  @Bean
  RedisMessageListenerContainer redisContainer3() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName(host3);
    jedisConnectionFactory.setPort(port3);
    jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
    jedisConnectionFactory.afterPropertiesSet();
    container.setConnectionFactory(jedisConnectionFactory);
    return container;
  }

  @Bean
  RedisMessageListenerContainer redisContainer4() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName(host4);
    jedisConnectionFactory.setPort(port4);
    jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
    jedisConnectionFactory.afterPropertiesSet();
    container.setConnectionFactory(jedisConnectionFactory);
    return container;
  }

  @Bean
  RedisMessageListenerContainer redisContainer5() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName(host5);
    jedisConnectionFactory.setPort(port5);
    jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
    jedisConnectionFactory.afterPropertiesSet();
    container.setConnectionFactory(jedisConnectionFactory);
    return container;
  }

  @Bean
  RedisMessageListenerContainer redisContainer6() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName(host6);
    jedisConnectionFactory.setPort(port6);
    jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
    jedisConnectionFactory.afterPropertiesSet();
    container.setConnectionFactory(jedisConnectionFactory);
    return container;
  }

  @Bean
  RedisKeyExpirationListener redisKeyExpirationListener1() {
    return new RedisKeyExpirationListener(redisContainer1());
  }

  @Bean
  RedisKeyExpirationListener redisKeyExpirationListener2() {
    return new RedisKeyExpirationListener(redisContainer2());
  }

  @Bean
  RedisKeyExpirationListener redisKeyExpirationListener3() {
    return new RedisKeyExpirationListener(redisContainer3());
  }

  @Bean
  RedisKeyExpirationListener redisKeyExpirationListener4() {
    return new RedisKeyExpirationListener(redisContainer4());
  }

  @Bean
  RedisKeyExpirationListener redisKeyExpirationListener5() {
    return new RedisKeyExpirationListener(redisContainer5());
  }

  @Bean
  RedisKeyExpirationListener redisKeyExpirationListener6() {
    return new RedisKeyExpirationListener(redisContainer6());
  }

}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import java.util.Date;

/**
 * @Author xiabing5
 * @Create 2019/9/4 9:47
 * @Desc  redis過期監聽
 **/
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
  @Autowired
  RedisUtil redisUtil;
  @Autowired
  LoginUserStatisticsMapper loginUserStatisticsMapper;
  public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
    super(listenerContainer);
  }
  @Override
  public void onMessage(Message message,byte[] pattern) {
    // 使用者做自己的業務處理即可,message.toString()可以獲取失效的key
    String mesg = message.toString();   
  }
}

3. Redis防止過期key重複監聽

對於專案叢集情況下,部署多個服務後,容易出現redis過期被多個服務同時監聽到,從而執行相同的業務邏輯,這不是我們期望的。單機部署下方法的同步可以採用synchronize關鍵字。但叢集下,就得采用分散式鎖。在需要加鎖的地方,只要加鎖和解鎖即可。此處正好寫到Redis,那就貼一個自己用的redis分散式鎖。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Collections;
import java.util.UUID;
/**
 * @Author xiabing5
 * @Create 2019/9/6 15:54
 * @Desc  redis分散式鎖
 **/
@Component
public class RedisLock {
  @Autowired
  Jedis jedis;
  private static final String SET_IF_NOT_EXIST = "NX"; // NX表示如果不存在key就設定value
  private static final String SET_WITH_EXPIRE_TIME = "PX"; // PX表示毫秒
  // 加鎖
  public String tryLock(String key,Long acquireTimeout) {
    // 生成隨機value
    String identifierValue = UUID.randomUUID().toString();
    // 設定超時時間
    Long endTime = System.currentTimeMillis() + acquireTimeout;
    // 迴圈獲取鎖
    while (System.currentTimeMillis() < endTime) {
      String result = jedis.set(key,identifierValue,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,acquireTimeout);
      if("OK".equals(result)) {
        return identifierValue;
      }
    }
    return null;
  }
  // 解鎖
//  public void delLock(String key,String identifierValue) {
//    // 判斷是否是同一把鎖
//    try{
//      if(jedis.get(key).equals(identifierValue)){
//        // 此處操作非原子性,容易造成釋放非自己的鎖
//        jedis.del(key);
//      }
//    }catch(Exception e) {
//      e.printStackTrace();
//    }
//  }
  // 使用Lua程式碼解鎖
  public void delLock(String key,String identifierValue) {
    try{
      String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
      Long result = (Long) jedis.eval(script,Collections.singletonList(key),Collections.singletonList(identifierValue));
      if (1 == result) {
        System.out.println(result+"釋放鎖成功");
      } if (0 == result) {
        System.out.println(result+"釋放鎖失敗");
      }
    }catch (Exception e) {
      e.printStackTrace();
    }
  }
}

4. 總結

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

以上所述是小編給大家介紹的Redis叢集下過期key監聽的實現程式碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對我們網站的支援!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!