[Spring Boot] Redis Key Expired Event

Redis에서 TTL 설정 된 key에 대해 expire 될 때 이벤트를 받을 필요가 생겨 리서치를 진행 했다.  Reids 버전 언제부터인지는 모르겠지만 아래 redis.conf를 보니 key expired 이벤트를 받을 수 있도록 되어 있었다.


출처 : https://github.com/redis/redis/blob/unstable/redis.conf

expired 이벤트는 disabled가 default라 아래처럼 redis node에서 따로 설정을 해 줘야 한다.
$> config set notify-keyspace-events Ex
note) 맨 뒤 Ex는 위 redis.conf에 있는 E : Keyevent events / x : Expired events 두 개를 사용하겠다는 의미.

Redis 서버에 셋팅 후 Spring Boot에서 이벤트를 받아 보기 위해 코드를 작성했는데 두 가지가 가능 했다. 첫째로 Redis의 KeyExpirationEventMessageListener와 Spring Boot EventListener를 활용하는 방법과 두번째는 그냥 Redis의 MessageListener를 구현하는 방법이다.

1) Spring EventListner

RedisKeyExpiredEvent를 받는 리스너를 만들어 준다.
@Component
public class MyRedisKyeExpiredSpringListener {
    @EventListener
    public void expiredKey(RedisKeyExpiredEvent event) {
        System.out.println("=========== spring listener ===========");
        System.out.println("event : " + event);
        System.out.println("event varlue : " + event.getValue());
        System.out.println("=======================================");
    }
}

그리고 기존 Redis 설정 부분에 KeyExpirationEventMessageListener를 bean으로 등록만 해 주면 RedisMessageListenerContainer에 알아서 추가가 된다.
    @Bean
    public KeyExpirationEventMessageListener keyExpirationEventMessageListener() {
        return new KeyExpirationEventMessageListener(redisMessageListenerContainer());
    }

redis-cli로 3초 TTL key를 만들어 보면,
127.0.0.1:6379> set mykey "test" ex 3
3초 뒤 Redis expired 이벤트가 수신되고 ApplicationEventPublisher를 통해 @EventListner로 전달 되는걸 볼 수 있다.

2) MessageListener Override

아래처럼 MessageListener를 구현하고,
@RequiredArgsConstructor
public class MyRedisListener implements MessageListener {
    @Override
    public void onMessage(final Message message, final byte[] pattern) {
        System.out.println("================== normal ====================");
        System.out.println("message : " + message);
        System.out.println("message channel : " + message.getChannel());
        System.out.println("message channel str : " + new String(message.getChannel()));
        System.out.println("message body : " + message.getBody());
        System.out.println("message body str : " + new String(message.getBody()));

        System.out.println("pattern : " + pattern);
        System.out.println("pattern str : " + new String(pattern));
        System.out.println("==============================================");
    }
}

Redis 기존 설정에 adapter와 container 설정을 추가해 주면 1과 마찬가지로 해당 이벤트를 받아 볼 수 있다.
    public static final String EXPIRED_PATTERN = "__key*__:expired";

    @Bean
    public MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(new MyRedisListener());
    }

    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory());
        container.addMessageListener(messageListenerAdapter(), new PatternTopic(EXPIRED_PATTERN));
        return container;
    }

참고로 keyspacekeyevent라는 용어가 좀 헷갈려 Redis 공식 문서를 참고해 보았다.
  • keyspace : key에 대한 event를 수신
  • keyevent : event에 대한 key를 수신
무슨 말인지 더 알기 어려운데 공식 문서의 내용을 가져와 보면
PUBLISH __keyspace@0__:mykey del
PUBLISH __keyevent@0__:del mykey
위와 같이 keyspace는 이벤트인 del이 수신되고 keyevent는 mykey가 수신이 된다. Redis에서 받은 expired 이벤트의 내용을 로그로 출력해 보면 더 이해가 쉬울 것이다. 기억 해 둘만한 것은 key에 대한 value는 받지 못한다는 점.


AWS ElastiCache에도 같은 방법이니 참고 해 보자.


댓글

이 블로그의 인기 게시물

[Protocol] WIEGAND 통신

Orange for Oracle에서 한글 깨짐 해결책

[URL] 대소문자를 구분하나?