Kafka time wheel depth analysis: principle, source code and application scenarios
Table of contents
- Introduction: Challenges of timing task processing
-
Analysis of the core principle of time wheel
- 2.1 Basic concepts and data structures
- 2.2 Level time round design
-
Source code analysis: Kafka time round implementation
- 3.1 Core Class Structure Analysis
- 3.2 Task addition and execution process
- 3.3 Time round advancement mechanism
- 3.4 The key role of DelayQueue
- Typical application scenarios
- Summary and performance comparison
1. Introduction: Challenges of timing task processing
In distributed systems, timing task management (such as delayed messages and heartbeat detection) needs to meet two core needs:High precisionandHigh throughput. Traditional solutions such as priority queues (O(log n) time complexity) have dramatically declined in the million-level task scenario. Kafka adoptsTiming WheelThe algorithm realizes O(1) time complexity, and a single machine supports millions of timed tasks. The time wheel achieves a qualitative performance breakthrough in timing task processing through ring queues and hash ideas.
2. Analysis of the core principles of the time wheel
2.1 Basic concepts and data structures
-
Data structure disassembly:
-
Time slot (Bucket):
- Each slot corresponds to a time interval (
tickMs
, such as 1ms) - useTwo-way link list(
TimerTaskList
) Management tasks in the slot - Example: If
tickMs=1ms
,wheelSize=20
, then the total span of the time roundinterval=20ms
- Each slot corresponds to a time interval (
-
Pointer advance logic:
- Initial time pointer
currentTime
Point to the current slot start time - Every time you advance,
currentTime
according totickMs
Increment -
Alignment mechanism: Pointer time is always
tickMs
integer multiples ofcurrentTime = (startMs / tickMs) * tickMs
)
- Initial time pointer
-
Task hash positioning:
- Calculate the difference between the task expiration time and the pointer:
expirationMs - currentTime
- Determine the slot index:
(expirationMs / tickMs) % wheelSize
- Hash conflict handling: Tasks in the same slot are processed in the order of linked list
- Calculate the difference between the task expiration time and the pointer:
Summarize: Time round passesHash bucket + pointer slidingImplement batch processing of tasks, and the time complexity is stable to O(1).
-
Time slot (Bucket):
2.2 Level time round design
Kafka usesMulti-level time round(Similar to the hour/minute hand collaboration):
- Bottom wheel: high precision small range (such as second level)
- Upper wheel: low precision and large range (such as minute level)
- Task downgrade: Resubmit to the lower level after the upper level turn expires
Hierarchical collaboration process:
-
Example of hierarchical parameters:
- Level 1 (bottom level):
tickMs=1ms
,wheelSize=20
,interval=20ms
- Level 2:
tickMs=20ms
,wheelSize=60
,interval=1200ms
- Level 3:
tickMs=1200ms
,wheelSize=60
,interval=72000ms
- Level 1 (bottom level):
-
Overflow Handling:
- When the task delay exceeds the current time round
interval
When submitting to the upper time round - The slots of the upper time wheel represent the full cycle of the lower time wheel
- Example: Each slot in layer 2 (20ms) corresponds to the full 20ms cycle of layer 1
- When the task delay exceeds the current time round
-
Pointer linkage mechanism:
- When the upper time wheel pointer advances, the tasks in its slot will recalculate the hash, which may be downgraded to the underlying time wheel.
# Task Add Process Pseudo Code
void add_task(task):
if < current_wheel.interval:
Put into the slot corresponding to the current time wheel
else:
Recursively submitting to the upper time round
Summarize: The hierarchical time round passesTime range is enlarged layer by layerandTask recursive downgrade, realizes unified management of delay tasks from milliseconds to hourly levels, and hierarchical design expands the time range while maintaining accuracy, similar to the multi-level time hierarchy idea of CPU cache.
3. Source code analysis: Kafka time round implementation
3.1 Core Class Structure Analysis
// Delay task
class TimerTask {
private final long delayMs; //Delay time
private final Runnable task; //Delay task
protected TimerTaskList timerTaskList; //Time slot
protected TimerTask next; //Next node
protected TimerTask prev; //Previous node
}
// Task queue, task bidirectional link table
class TimerTaskList implements Delayed {
private final AtomicLong expire;// Expiry time
private final TimerTask root; //root node
public TimerTaskList(){
expire = new AtomicLong(-1L);
root = new TimerTask( null,-1L);
= root;
= root;
}
//Add a new task, add the task to the head of the two-way linked list
public void addTask(TimerTask timerTask) {
synchronized (this) {
if ( == null) {
= this;
TimerTask tail = ;
= root;
= tail;
= timerTask;
= timerTask;
}
}
}
//Remove task
public void removeTask(TimerTask timerTask) {
synchronized (this) {
if (()) {
= ;
= ;
= null;
= null;
= null;
}
}
}
}
// Key parameters of Kafka time round class
class TimingWheel {
private long tickMs; // Time slot accuracy (such as 1ms)
private int wheelSize; // Total number of time slots
private long interval; // Total time range = tickMs * wheelSize
private List<TimerTaskList> timerTaskList; // ring queue
private volatile TimingWheel overflowWheel; //Upper level time round
private final Consumer<TimerTaskList> consumer;//Task Processor
}
Summarize: manage time slots through two-way linked lists, and combine JDK's delay queue DelayQueue to achieve efficient task downgrade and time-wheel drive.
3.2 Task addition process
// Core entrance
public boolean addTask(TimerTask timerTask) {
long expiration = ();
//Expired tasks are executed directly
if (expiration < currentTime + tickMs) {
return false;
} else if (expiration < currentTime + interval) {
//The current time wheel can accommodate this task. Add to the time slot.
long virtualId = expiration / tickMs;
int index = (int) (virtualId % wheelSize);
TimerTaskList timerTaskList = timerTaskLists[index];
(timerTask);
if ((virtualId * tickMs)) {
//Add to delayQueue
(timerTaskList);
}
} else {
//The time round placed on the previous level
TimingWheel timeWheel = getOverflowWheel();
(timerTask);
}
return true;
}
//Get the upper time round
private TimingWheel getOverflowWheel() {
if (overflowWheel == null) {
synchronized (this) {
if (overflowWheel == null) {
overflowWheel = new TimingWheel(interval, wheelSize, currentTime, consumer);
}
}
}
return overflowWheel;
}
-
Time alignment:pass
virtualId * tickMs
Calculate the exact expiration time of the slot -
Delayed queue association: Add the slot only if the first time it is added to the task
DelayQueue
-
Lazy loading of the upper time round:pass
getOverflowWheel()
Methods to create upper time round as needed -
Thread safety control:
currentTime
useAtomicLong
Ensure visibility
Summarize: When adding tasks, find the appropriate slot through the time round step by step, and send the task directly when it expires.
3.4 The key role of DelayQueue
Implementation details:
-
Slot Packaging:Each
TimerTaskList
accomplishDelayed
Interfaces, sorted by slot expiration time -
Efficient wake-up:
()
Wake up the thread immediately when the slot expires to avoid CPU idling - Batch processing: A slot may contain hundreds of tasks, reducing lock competition
public long getDelay(TimeUnit unit) {
return (0, (() - (), ));
}
Summarize:DelayQueue
It is the "heartbeat engine" of the time wheel, driving the pointer to advance as needed.
3.3 Time round advancement mechanism
Driver core: Background thread obtains the expiration time slot through DelayQueue
public void advanceClock(long timestamp) {
if (timestamp >= currentTime + tickMs) {
currentTime = timestamp - (timestamp % tickMs);
if (overflowWheel != null) {
//Promote the upper time round time
().advanceClock(timestamp);
}
}
}
Summarize: Batch processing expired tasks reduce context switching by triggering time round propulsion by delaying queues.
4. Typical application scenarios
- Delayed message: Implement accurate delayed message delivery (such as order timeout)
- Session timeout: Consumer group heartbeat detection and Rebalance
- Request timeout: Timeout control for processing Produce/Fetch requests
- Timed indicator collection: Statistics of Broker Performance Indicators
Summarize: The time round is Kafka's core infrastructure for achieving low latency and high throughput.
5. Summary and performance comparison
plan | Time complexity | Time-consuming for inserting million tasks | Applicable scenarios |
---|---|---|---|
Priority queue | O(log n) | ~3ms | Low concurrency timing tasks |
Time round | O(1) | ~0.2ms | High concurrency delay operation |
Performance optimization tips:
- Time slot pre-allocation: Avoid memory allocation overhead when adding tasks
- Pointer jumping propulsion: Skip empty slot time without task
- Batch expiration processing: Merge multiple small tasks into the same slot
Core advantages:
- Time complexity is stable to O(1)
- Batch processing reduces thread competition
- Level design takes into account both precision and range
Philosophical Inspiration for Design:
- Change time in space: Exchange O(1) time complexity by pre-allocating slot memory
- Stratified governance: Different levels deal with problems of different sizes (similar to JVM memory generation)
Through layer by layer source code analysis, it can be seen that the Kafka time round isAlgorithm optimizationandEngineering PracticeA model of combination. Its design idea is not only applicable to message queues, but also has important reference value for any system that requires high concurrent timing tasks.