How to Build Redis-Based Delayed Queues: 3 Practical Approaches
This article explains three common methods for implementing delayed queues with Redis—using key expiration notifications, Zset with scheduled tasks, and Redisson’s RDelayedQueue—detailing their concepts, core code examples, advantages, and limitations for backend developers.
Redis is a widely used middleware in many projects. Besides distributed locks and caching, it can also be used to implement delayed queues. The following three common Redis‑based delayed‑queue solutions are introduced.
1. Using Expired‑Key Notifications
Implementation idea: enable Redis key‑space expiration notifications, set a key with a TTL in the business logic, and let Redis automatically push an expiration message to a listener when the key expires, thereby triggering a delayed task.
<code>#1 Enable key‑space expiration notifications
notify-keyspace-events Ex
#2 Listener for expired keys (Java)
@Component
@Slf4j
public class RedisExpireKeyService extends KeyExpirationEventMessageListener {
public RedisExpireKeyService(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
String expireKey = message.toString();
// Execute business logic
System.out.println("Detected expired key=" + expireKey);
}
}
</code>This approach is not recommended for production because Redis uses a combination of lazy and periodic deletion, so a key may not be removed immediately at expiration.
2. Using Zset + Scheduled Task
Implementation idea: ZSet is an ordered set that stores unique elements with a double‑type score. By using the expiration timestamp as the score, a scheduled task can scan the ZSet and process elements whose score has passed, achieving a delayed queue.
<code># Using XXL‑Job (Java)
@JobName("consumerTaskJob")
public void consumerTaskJob() {
String expireKey = "ExPIRE_KEY";
try {
double currentTime = System.currentTimeMillis();
Set<String> expiredMemberSet = redisTemplate.opsForZSet()
.rangeByScore(expireKey, Double.MIN_VALUE, currentTime);
for (String expiredMember : expiredMemberSet) {
// TODO: execute actual delayed task
redisTemplate.opsForZSet().remove(expireKey, expiredMember);
}
} catch (Exception e) {
log.error("Data processing failed", e);
}
}
</code>Although more reasonable than the expired‑key listener, this method still has drawbacks such as lack of retry mechanisms, fixed delay tied to the scheduler, and poor scalability for large numbers of delayed tasks.
3. Redisson Delayed Queue
Redisson is a Java client for Redis that provides RDelayedQueue and RQueue interfaces. The delayed queue is internally based on a ZSet.
<code>#1 Add Maven dependency
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.0</version>
</dependency>
#2 Add data to the queue
RedissonClient redissonClient = Redisson.create();
RBlockingDeque<String> queue = redissonClient.getBlockingDeque("delayQueue");
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(queue);
// Add a delayed task (5 seconds)
delayedQueue.offer("Task1", 5000, TimeUnit.MILLISECONDS);
#3 Consume data
while (true) {
try {
String task = queue.take(); // blocks if empty
System.out.println("Task: " + task);
} catch (Exception e) {
log.error("Consumption failed", e);
}
}
</code>Conclusion
Redis‑based delayed queues are suitable for simple business scenarios such as sending emails or notifications. For more complex or large‑scale delayed‑task requirements, Redis may not be the best choice.
Lobster Programming
Sharing insights on technical analysis and exchange, making life better through technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.