I was trying to figure out how to implement some sort of timeout mechanism to discard any task if that task surpassed the maximum wait time, and the wait time can be configurable in seconds, minutes, hours, and may vary from customer to customer.
Then I came across notifications events in Redis. This article will give you an overview of how I have used Redis keyspace notifications events with the spring boot application to achieve the functionality.
Basically the idea here is whenever a new task came in, set a key in Redis with some prefix along with task metadata and expiry as the configured max wait time, and then by using Redis Keyspace Notifications for Expired Keys listen to the key expiry event, and based on that discard the task.
Enable keyspace notifications for Expired key Only:
IMPORTANT Keyspace notifications feature is available in Redis server version 2.8.0 or higher.
By default keyspace, notifications are disabled in Redis to avoid unnecessary CPU utilization. There are two ways we can enable this either using redis-cli or redis.conf file.
Enable Using Redis-CLI:
Subhams-MBP-2:~ subhammajavadiya$ redis-cli
127.0.0.1:6379> config set notify-keyspace-events Ex
OK
Enable Using Redis.conf:
By adding the below line in redis.conf and restarting the redis-server.
notify-keyspace-events Ex
I would recommend enabling from redis.conf file in the production environment to avoid the config reset if redis-server gets restarted.
Create Task timeout listener
- To listen to the key expire event in spring boot application I have added spring.data.redis dependency in the build.gradleand implemented TaskTimeOutListener as MessageListener bean.
@Slf4j
@Component
public class TaskTimeOutListener implements MessageListener { @Override
public void onMessage(Message message, byte[] pattern) {
String body = new String(message.getBody());
String channel = new String(message.getChannel());log.info("Received task timeout event: {} for key: {}", body, channel);
String expiredKey = channel.split(":")[1];
//implemented logic to discard task
}}
- Register TaskTimeOutListener, MessageListener bean with RedisMessageListenerContainer to listen the key expiry event with a specific pattern i.e task_with_max_wait_time__*
@Slf4j
@Configuration
public class RedisConfiguration { @Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
TaskTimeOutListener taskTimeoutListener) {
RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory);
listenerContainer.addMessageListener(maxWaitTimeExpiryListener,
new PatternTopic("__key*__:task_with_max_wait_time__*"));
listenerContainer.setErrorHandler(
e ->log.error("Error in redisMessageListenerContainer", e));
return listenerContainer;
}
}
- Let’s start the spring boot application and from redis-cli set a task with expiry time as 10 sec with key task_with_max_wait_time__1.
Subhams-MBP-2:~ subhammajavadiya$ redis-cli
127.0.0.1:6379> SETEX task_with_max_wait_time__1 10 "test task"
OK
- After 10 seconds
{"@timestamp":"2020-06-12T19:28:48.063+00:00","@version":1,"message":"Received task timeout event: expired, for key :task_with_max_wait_time__1","logger_name":"com.test.redislistener.TaskTimeOutListener",
"thread_name":"redisMessageListenerContainer-2","level":"INFO","level_value":20000,"app_name":"test-app","log_type":"app"}
And, it's up and running. Hope this helps you.