Implementing a Delayed Queue with Redis Zset in Java Spring Boot
This article explains the concept of delayed queues, outlines typical use cases, compares implementation options, and provides a complete Java Spring Boot example that uses Redis Zset to create, manage, and execute delayed tasks with accompanying code snippets and test results.
First, a delayed queue is a data structure similar to a normal FIFO queue but each element carries a specified delay time, causing it to be processed only after that time has elapsed; it can be viewed as a time‑weighted ordered heap.
Application Scenarios
Automatically cancel an order if payment is not received within X minutes after successful placement.
Send a reminder to a delivery person shortly before an order’s timeout.
Notify participants a few minutes before a scheduled meeting starts.
Other time‑based trigger scenarios.
Possible Solutions
JDK DelayQueue API.
Quartz scheduler.
Redis Zset (the method demonstrated here).
Message queue systems.
Other custom implementations.
Implementation Using Redis Zset
The following example uses Redis Zset to implement a delayed queue.
zset is a special Redis data type that combines a set (ensuring unique values) with a score attribute; sorting by score provides an ordered collection, useful for time‑based ordering such as delayed tasks.
1. pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>2. application.yml
spring:
redis:
host: localhost
port: 6379
password: xxx
database: 13. Delayed task interface
public interface RedisDelayTask {
String getId();
String getValue();
long getDelayTime();
void execute();
}4. Abstract task implementation
public abstract class AbstractRedisDelayTask implements RedisDelayTask {
protected String id;
protected String value;
private long delayTime;
public AbstractRedisDelayTask(String id, String value, long delayTime) {
this.id = id;
this.value = value;
this.delayTime = delayTime;
}
@Override public String getId() { return id; }
@Override public String getValue() { return value; }
@Override public long getDelayTime() { return delayTime; }
@Override public String toString() {
return "RedisDelayTask{id='" + id + "', value='" + value + "', delayTime=" + delayTime + "}";
}
}5. Concrete notification task
public class NoticeTask extends AbstractRedisDelayTask {
private static final Logger LOGGER = LoggerFactory.getLogger(NoticeTask.class);
public NoticeTask(String id, String value, long delayTime) {
super(id, value, delayTime);
}
@Override public void execute() {
LOGGER.info("task execute, {}", this);
}
}6. Task manager
@Component
public class RedisDelayQueueManager implements InitializingBean {
@Autowired
private StringRedisTemplate redisTemplate;
private Map
tasks = new ConcurrentHashMap<>();
public void addTask(RedisDelayTask task) {
long delayedTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(task.getDelayTime(), TimeUnit.SECONDS);
boolean r = redisTemplate.opsForZSet().add(task.getId(), task.getValue(), delayedTime);
if (r) { tasks.put(task.getId(), task); }
}
private void checkAndExecuteTask() {
while (true) {
for (String taskId : tasks.keySet()) {
Set
> tuples = redisTemplate.opsForZSet()
.rangeByScoreWithScores(taskId, 0, System.currentTimeMillis());
if (!CollectionUtils.isEmpty(tuples)) {
for (ZSetOperations.TypedTuple
tuple : tuples) {
RedisDelayTask task = tasks.remove(taskId);
if (task != null) {
task.execute();
redisTemplate.opsForZSet().remove(taskId, tuple.getValue());
}
}
}
}
}
}
@Override public void afterPropertiesSet() throws Exception {
new Thread(this::checkAndExecuteTask, "redis-delay-task").start();
}
}7. Test case
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedisApplication.class)
public class RedisDelayTaskTest {
@Autowired
private RedisDelayQueueManager redisDelayQueueManager;
@Test
public void addTask() throws IOException {
NoticeTask task = new NoticeTask("notice-task", "notice-task-value", 5);
redisDelayQueueManager.addTask(task);
NoticeTask task2 = new NoticeTask("notice-task2", "notice-task-value2", 10);
redisDelayQueueManager.addTask(task2);
System.in.read();
}
}Execution Result
2022-01-22 17:27:58.140 INFO [edis-delay-task] c.springboot.demo.redis.task.NoticeTask : task execute, RedisDelayTask{id='notice-task', value='notice-task-value', delayTime=5} 2022-01-22 17:28:03.925 INFO [edis-delay-task] c.springboot.demo.redis.task.NoticeTask : task execute, RedisDelayTask{id='notice-task2', value='notice-task-value2', delayTime=10}
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.