본문 바로가기
redis

[SpringBoot] Redis Key Expired Event Notification

by moonsiri 2021. 4. 1.
728x90
반응형

Redis에서 만료된 키를 수신하거나 subscribe 하는 방법을 알아보다가 Redis에서 알림 이벤트를 발견하였습니다.

만료된 키에 대한 Redis Keyspace 알림에 대해 알아보겠습니다.

 

우선 redis.conf를 사용하여 keyspace 알림을 활성화하겠습니다.

/etc 하위에 존재하는 redis.confnotify-keyspace-events Ex를 추가합니다.

 

더보기

이벤트 종류

  • K   Keyspace events, publish prefix "__keyspace@<db>__:".
  • E   Keyevent events, publish prefix "__keyevent@<db>__:".
  • g   공통 명령: del, expire, rename, ...
  • $   스트링(String) 명령
  • l   리스트(List) 명령
  • s   셋(Set) 명령
  • h   해시(Hash) 명령
  • z   소트 셋(Sorted set) 명령
  • x   만료(Expired) 이벤트 (키가 만료될 때마다 생성되는 이벤트)
  • e   퇴출(Evicted) 이벤트 (최대메모리 정책으로 키가 삭제될 때 생성되는 이벤트)
  • A   모든 이벤트(g$lshzxe), "AKE"로 지정하면 모든 이벤트를 받는다.

 

AWS의 Elasticache를 사용한다면 https://aws.amazon.com/ko/premiumsupport/knowledge-center/elasticache-redis-keyspace-notifications/ 를 참조하여 Ex 값을 넣어주세요.

 

 

 

 

redis 재시작

$ sudo systemctl restart redis

 

설정을 완료하였으니 기능이 잘 작동하는지 테스트해보겠습니다.

만료된 이벤트에 대한 subscribe를 추가합니다.

 

만료 시간을 5초로 설정하고 값을 설정해보겠습니다.

 

5초 후

 

 


 

pom.xml에 dependency를 추가합니다.

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

 

RedisListenerConfiguration.java

@Configuration
public class RedisListenerConfiguration {

	@Bean
	public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
		RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
		redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
		return redisMessageListenerContainer;
	}
    
}

 

RedisKeyExpiredListener.java

@Component
public class RedisKeyExpiredListener extends KeyExpirationEventMessageListener {

	/**
	 * Creates new {@link MessageListener} for {@code __keyevent@*__:expired} messages.
	 *
	 * @param listenerContainer must not be {@literal null}.
	 */
	public RedisKeyExpiredListener(@Qualifier("redisMessageListenerContainer") RedisMessageListenerContainer listenerContainer) {
		super(listenerContainer);
	}

	/**
	 *
	 * @param message   redis key
	 * @param pattern   __keyevent@*__:expired
	 */
	@Override
	public void onMessage(Message message, byte[] pattern) {

		System.out.println("########## onMessage pattern " + new String(pattern) + " | " + message.toString());
	}
}

 

 

5초 후

 

 

기본적으로 expired 이벤트는 레디스 서버가 키를 삭제할 때 발생되는 이벤트고, 이론적으로 TTL이 0으로 도달했을 때는 발생하지 않습니다.

만약 강제로 expired 이벤트를 실행하고 싶다면 ttl을 1로 설정하시면 됩니다.

redisCommands.expire(key, 1);

 

 

 

더보기

RedisCommandExecutionException: ERR unknown command `CONFIG`, with args beginning with: `SET`, `notify-keyspace-events`, `Ex`

 

위 오류 발생시 해결방법

1.

	@Bean
	public ConfigureRedisAction configureRedisAction(){
		return ConfigureRedisAction.NO_OP;
	}

 

2.

/**
 * Elastic Cache 사용을 위한 KeyspaceEventMessageListener 커스텀 클래스
 *  - config command 사용 방지.
 *  - Elastic Cache 에서 사용할 수 없는 command. https://docs.amazonaws.cn/en_us/AmazonElastiCache/latest/red-ug/RestrictedCommands.html
 *
 */
@Slf4j
public abstract class CustomKeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean {
    
    /**
     * Initialize the message listener by writing requried redis config for {@literal notify-keyspace-events} and
     * registering the listener within the container.
     */
    public void init() {
        if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {

            RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();

            try {
// ## Elastic Cache에서 https://docs.amazonaws.cn/en_us/AmazonElastiCache/latest/red-ug/RestrictedCommands.html 정책으로 config command 사용 불가. Sass parameter group에 설정된 값 사용
//                Properties config = connection.getConfig("notify-keyspace-events");
//                log.info("### notify-keyspace-events : {}", config.getProperty("notify-keyspace-events"));
//
//                if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
//                    connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
//                }

                log.info("### Skip redis 'config' command because of not supporting on elastic cache(AWS. restriction command) ###");
            } finally {
                connection.close();
            }
        }

        doRegister(listenerContainer);
    }

	// 나머지는 KeyspaceEventMessageListener와 동일
}
/**
 * Elastic Cache 사용을 위한 KeyspaceEventMessageListener 커스텀 클래스
 *
 */
public class CustomKeyExpirationEventMessageListener extends CustomKeyspaceEventMessageListener implements ApplicationEventPublisherAware {
    // KeyExpirationEventMessageListener과 동일
}
@Component
public class RedisKeyExpiredListener extends CustomKeyExpirationEventMessageListener {
	// KeyExpirationEventMessageListener 대신 CustomKeyExpirationEventMessageListener 상속
}

 

 

 

 

[Reference]

redis.io/topics/notifications

sauravomar01.medium.com/redis-keyspace-notifications-for-expired-keys-f38c18484a89

programmersought.com/article/79356560309/

docs.spring.io/spring-session/docs/current/reference/html5/#api-redisindexedsessionrepository-sessiondestroyedevent

728x90
반응형

댓글