Application of Time Wheel Algorithm in Dubbo

Set

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

zzPROZEXPIRY3z

zzPROZEXPIRY3z

zzPROZEXPIRY3z

zzPROCZEXPIRY3z

PER_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 zshed3zzzzzzzheel0z

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