as " star ", don't miss a good article!
1 Timing tasks
Netty, Quartz, Kafka and Linux all have timing tasks. The java.util.Timer and DelayedQueue that come with
JDK can implement simple timing tasks. The bottom layer uses a heap, and the access complexity is O(nlog(n)), but it cannot support massive timing tasks.
uses a time wheel algorithm in order to reduce the time complexity of task access and cancellation operations to O(1) in scenarios with large tasks and high performance requirements.
2 Time wheel model and its application
is a scheduling model for efficient batch management of timed tasks. It is generally implemented as a ring structure, similar to a clock, divided into many slots, one slot represents a time interval, and each slot uses a doubly linked list to store timing tasks. The
pointer jumps periodically, and when it jumps to a slot, the timing task of that slot is executed.
Hashed Timing Wheel structure diagram
applicable scenarios
failure recovery
flow control
scheduling algorithm
control the data packet life cycle in the network
timer is expensive to maintain, if the
processor will interrupt every clock tick
uses fine granularity Timer
Unfinished timers Many
need efficient timer algorithms to reduce the overall interrupt overhead. The capacity and accuracy of the
single-layer time wheel are limited. For scenarios that require extremely high accuracy, a very large time span, or a large number of timed tasks need to be scheduled, multi-level time wheels and a combination of persistent storage and time wheels are usually used. Program.
model and performance indicators
model rules
client call:
START_TIMER (time interval, Request_ID, Expiry_Action)
STOP_TIMER (Request_ID)
timer tick call:
PER_TICK_BOOKKEEPING3z
PER_TICK_BOOKKEEPING3z
zzPROZEXPIRY3zzzPROZEXPIRY3zzzPROZEXPIRY3zzzPROCZEXPIRY3zPER_TICK_BOOKKEEPING0 The time required to end any of the above routines
3 Dubbo's time wheel structure
core interface
TimerTask
In Dubbo, all timed tasks must implement the TimerTask interface. Only one run() method is defined, and the input parameter is a Timeout interface object. The
Timeout
Timeout object has a one-to-one correspondence with the TimerTask object, similar to the relationship between the Future object returned by the thread pool and the task object submitted to the thread pool. Through the Timeout object,
can not only view the status of the timing task, but also operate the timing task (for example, cancel the associated timing task). Methods in the
Timeout interface: The
Timer interface defines the basic behavior of a timer. The core is: submit a timer task (TimerTask) and return the associated Timeout object, similar to submitting a task to a thread pool.
HashedWheelTimeout
HashedWheelTimeout is the only implementation of the Timeout interface and is an internal class of HashedWheelTimer. HashedWheelTimeout plays two roles: the node of the doubly linked list in the
time wheel, that is, the timer task TimerTask in the HashedWheelTimer container
timer task The handle (Handle) returned after the TimerTask is submitted to HashedWheelTimer,It is used to view and control the core fields
prev and next of the timed task
outside the time wheel. The doubly linked list is used to chain timeouts (timing tasks) in HashedWheelTimerBucket. Since it only acts on WorkerThread, there is no need for synchronization/volatile.
task, the actually scheduled task
deadline, the execution time of the scheduled task. When creating HashedWheelTimeout, specify the calculation formula of
: ns
state, the current state of the timed task
Optional state:
STATE_UPDATER is used to realize the atomicity of state changes.
remainingRounds, the number of remaining clock cycles of the current task. The length of time that the time wheel can represent is limited. When the time difference between the task due time and the current moment exceeds the time length of a single lap of the time wheel, a loop will appear. This field value is required to indicate the remaining clock cycle.
Core API
isCancelled()
isExpired()
state()
Check the current HashedWheelTimeout status
cancel() method
expire() method
zzzzzzzzzzzzzheel0z
zzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzz
cancel() The slot in the
time wheel is actually a container for caching and managing the doubly linked list. Each node in the doubly linked list is an object, which is also associated with a timed task.
HashedWheelBucket holds the first and last nodes of the doubly linked list-head and tail, plus each HashedWheelTimeout node holds predecessor and successor references, you can traverse the entire linked list forward and backward. The core API
addTimeout()
pollTimeout()
remove()
removes the specified HashedWheelTimeout node from the doubly linked list.
clearTimeouts()
cyclically calls the pollTimeout() method to process the entire doubly linked list, and returns all tasks that have not timed out or been cancelled.
expireTimeouts()
traverse all HashedWheelTimeout nodes in the doubly linked list. When processing expired timed tasks, they will be removed through the remove() method, and their expire() method will be called for execution; for cancelled tasks, they will be removed directly after being removed through the remove() method; for unexpired tasks, they will be Decrease the remainingRounds field (the number of remaining clock cycles) by one. The realization of
HashedWheelTimer
interface implements a timer through the time wheel algorithm. The
function
selects the corresponding slot according to the current time wheel pointer, iterates from the head of the linked list, and calculates each timing task: if
belongs to the current clock cycle, take it out and run it. If
does not belong to it, reduce the number of remaining clock cycles by one
core domain
workerState
The current state of the time wheel, with three optional values, which can be modified atomically.
startTime
The start time of the current time wheel, and the deadline field value of the timed task submitted to the time wheel is calculated with this timestamp as the starting point.
wheel
time wheel circular queue, each element is a slot. When the number of designated time wheel slots is n, the 2 closest to n will be taken upwardsPower value
timeouts, canceledTimeouts
HashedWheelTimer will process the data of these two queues before processing the doubly linked list of HashedWheelBucket:
timeouts queue
buffers the timed tasks in the external submission time round zz
cancelledTimeouts3 zz
cancelledTimeouts3 zz
cancelledTimeouts 3 zz
cancelled timeouts zzz0 zz3 queu 0 Pointer, a monotonically increasing counter
mask
mask with a step length of 1. You can locate the corresponding clock slot
ticksDuration
every time the time pointer adds 1 to the actual time, in nanoseconds.
pendingTimeouts
The total number of timed tasks remaining in the current time round.
workerThread
The thread that actually executes timing tasks inside the time wheel. The logic that
worker
actually executes the timing task is encapsulated in this Runnable object.
newTimeout()
submit a timed task. Before the timed task enters the timeouts queue, the start() method will be called to start the time wheel. The following two key steps will be completed:
determines the startTime field of the time wheel
starts the workerThread thread and starts execution worker task. After
calculates the deadline of the timed task according to startTime, finally can the timed task be encapsulated and added to the queue.
4 The execution process of one rotation of the time wheel pointer
:
time wheel pointer rotates, the time wheel cycle starts
to clean up the timed tasks that the user actively canceled. These timed tasks are recorded in the queue when the user cancels. Every time the pointer rotates, the time wheel will clear the queue
transfer the timing tasks cached in the timeouts queue to the corresponding slot in the time wheel
locate the corresponding slot according to the current pointer, and process the doubly linked list of the slot The timed task
detects the state of the time wheel. If the time wheel is in the running state, the above steps are executed cyclically, and the timing tasks are continuously executed. If the time wheel is in the stopped state, execute the following steps to obtain the unexecuted timing tasks and add them to the queue: traverse each slot in the time wheel and call the () method; call the timeouts queue that is not added to the slot in a loop poll()
finally cleans up the timed tasks that the user actively canceled in the canceledTimeouts queue.
5 The timed task application
is not directly used for periodic operations, but only submits a single timed task to the time wheel for execution. When the last task is completed, the newTimeout() method is called to submit the current task again. Perform this task in the next cycle. Even if there are GC, I/O blocking, etc. during the execution of the task, which causes the task to be delayed or stuck, there will not be the same task continuously submitted, resulting in the accumulation of tasks. The application of
Dubbo time wheel is mainly in the following aspects:
failed to retry, for example, Provider retry operation when registration failed to the registry, or failed retry when Consumer subscribed to the registry, etc.
periodic timing tasks, for example, Send heartbeat requests regularly, the processing of request timeouts, or the reconnection mechanism after the network connection is disconnected.
Refer to
https://zhuanlan.zhihu.com/p/32906730