Understanding the Time Wheel Mechanism in Dubbo and Redisson
The article explains why a time‑wheel scheduler outperforms traditional timers, describes single‑ and multi‑layer wheel designs, and walks through Apache Dubbo’s HashedWheelTimer implementation—including TimerTask, Timeout, bucket and worker components—showing its use in Dubbo heartbeats and Redisson lock renewal.
In everyday development, timed tasks are frequently encountered. This article introduces the concept of a time wheel, explains why it is preferred over traditional scheduled‑task mechanisms, and analyses the implementation of the time wheel used in Apache Dubbo (HashedWheelTimer) and Redisson.
Why use a time wheel? Three typical scenarios are presented:
Heartbeat detection in Dubbo – a periodic task that keeps the consumer‑provider connection alive.
Timeout handling for RPC calls – a task that checks whether a Future has exceeded its deadline.
Redisson distributed‑lock renewal – a watchdog that periodically extends the lock lease.
Traditional approaches (one thread per task or a single thread scanning all tasks) either waste resources under high concurrency or cause unnecessary CPU polling. The time wheel solves these problems by placing tasks into slots (buckets) that are processed only when the wheel’s pointer reaches the corresponding slot.
1. Single‑layer time wheel
Assume a wheel period of 1 second with 10 slots, each representing 100 ms. Tasks A (220 ms), B (410 ms) and C (1930 ms) are placed into slots 2, 4 and 9 respectively. When the wheel rotates to a slot, the tasks in that slot are examined. If a task’s remaining rounds > 0, the round counter is decremented; otherwise the task is executed.
2. Multi‑layer time wheel
To handle tasks whose delay exceeds the wheel period, a second layer with a larger period (e.g., 10 seconds) is added. Task C from the previous example resides in the second‑layer slot 1. When the first layer completes a full rotation, the second‑layer slot 1 is demoted to the first layer slot 9, where it will be executed after another rotation. This hierarchical design is used by Kafka.
3. Core components of Dubbo’s time wheel
TimerTask – the interface that user‑defined tasks implement. Example:
public interface TimerTask {
void run(Timeout timeout) throws Exception;
}Timeout – represents a scheduled task. Its sole implementation is HashedWheelTimeout , which stores the task, deadline, state, and links to the doubly‑linked list inside a bucket.
public interface Timeout {
Timer timer();
TimerTask task();
boolean isExpired();
boolean isCancelled();
boolean cancel();
}Key fields of HashedWheelTimeout include:
int ST_INIT = 0, ST_CANCELLED = 1, ST_EXPIRED = 2;
AtomicIntegerFieldUpdater
STATE_UPDATER;
HashedWheelTimer timer;
TimerTask task;
long deadline;
int state = ST_INIT;
long remainingRounds;
HashedWheelTimeout next, prev;
HashedWheelBucket bucket;Methods such as remove() , cancel() and expire() manipulate the linked list and task state.
void remove() {
HashedWheelBucket bucket = this.bucket;
if (bucket != null) {
bucket.remove(this);
} else {
timer.pendingTimeouts.decrementAndGet();
}
}HashedWheelBucket – a slot that holds a doubly‑linked list of HashedWheelTimeout objects. It provides addTimeout() , remove() , expireTimeouts(long deadline) and clearTimeouts() .
void addTimeout(HashedWheelTimeout timeout) {
assert timeout.bucket == null;
timeout.bucket = this;
if (head == null) {
head = tail = timeout;
} else {
tail.next = timeout;
timeout.prev = tail;
tail = timeout;
}
}Worker – a dedicated thread that drives the wheel. Its run() method repeatedly:
waits for the next tick,
processes cancelled tasks,
transfers pending tasks to appropriate buckets,
expires tasks in the current bucket,
advances the tick.
public void run() {
startTime = System.nanoTime();
startTimeInitialized.countDown();
do {
long deadline = waitForNextTick();
if (deadline > 0) {
int idx = (int) (tick & mask);
processCancelledTasks();
HashedWheelBucket bucket = wheel[idx];
transferTimeoutsToBuckets();
bucket.expireTimeouts(deadline);
tick++;
}
} while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
// cleanup omitted for brevity
}HashedWheelTimer – the public API that implements Timer . It creates the wheel array, starts the worker thread, and offers newTimeout() and stop() methods.
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();
start();
long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
timeouts.add(timeout);
return timeout;
}4. Applications in Dubbo and Redisson
HeartbeatTimerTask – submitted to the idle‑check timer ( IDLE_CHECK_TIMER ) to send heartbeat requests when a channel has been idle for longer than the configured interval.
private void startHeartBeatTask(URL url) {
if (!client.canHandleIdle()) {
int heartbeat = getHeartbeat(url);
long heartbeatTick = calculateLeastDuration(heartbeat);
this.heartBeatTimerTask = new HeartbeatTimerTask(cp, heartbeatTick, heartbeat);
IDLE_CHECK_TIMER.newTimeout(heartBeatTimerTask, heartbeatTick, TimeUnit.MILLISECONDS);
}
}Redisson lock renewal – a task that periodically calls renewExpiration() to extend the lock lease. The renewal task itself is scheduled via newTimeout() on Redisson’s internal timer.
private void renewExpiration() {
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
// omitted: fetch entry, call Lua script, reschedule if successful
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}Timeout retry – similar to the heartbeat task; the article leaves the concrete implementation to the reader.
5. Summary
The article first motivates the need for a time wheel through three real‑world examples, then explains single‑ and multi‑layer wheel designs, and finally dives into the core source code of Dubbo’s HashedWheelTimer (including TimerTask , Timeout , HashedWheelBucket , Worker , and HashedWheelTimer ). It also shows how Dubbo’s heartbeat mechanism and Redisson’s lock renewal leverage this data structure.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
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.