TimingWheel
This article introduces the delayed operation in go-zero
. Delayed operation, two options can be used:
Timer
: The timer maintains a priority queue, executes it at the time, and then stores the tasks that need to be executed in the map- The
timingWheel
incollection
maintains an array for storing task groups, and each slot maintains a doubly linked list of tasks. When the execution starts, the timer executes the tasks in a slot every specified time.
Scheme 2 reduces the maintenance task from the priority queue O(nlog(n))
to the doubly linked list O(1)
, and the execution of the task only needs to poll the tasks O(N)
at a time point. Priority queue, put and delete elements O(nlog(n))
.
#
timingWheel in cacheFirst, let's first talk about the use of TimingWheel in the cache of collection:
This is the initialization of cache
and the initialization of timingWheel
at the same time for key expiration processing. The parameters in turn represent:
interval
: Time division scalenumSlots
: time slotsexecute
: execute a function at a point in time
The function executed in the cache
is to delete the expired key, and this expiration is controlled by the timingWheel
to advance the time.
Next, let's learn about it through the use of timingWheel by cache.
#
InitializationThe above is a more intuitive display of the "time wheel" of the timingWheel
, and the details of the advancement will be explained later around this picture.
go tw.run()
opens a coroutine for time promotion:
It can be seen that the timer
execution is started at the time of initialization, and it is rotated in the internal
time period, and then the bottom layer keeps getting the tasks from the list
in the slot
and handing them over to the execute
for execution.
#
Task OperationThe next step is to set the cache key
:
- First look at whether this key exists in the
data map
- If it exists, update
expire
->MoveTimer()
- Set the key for the first time ->
SetTimer()
So the use of timingWheel
is clear. Developers can add
or update
according to their needs.
At the same time, when we follow the source code, we will find that: SetTimer() MoveTimer()
all transports tasks to channel, and the coroutine opened in run()
continuously takes out the task operations of channel
.
SetTimer() -> setTask()
:
- not exist task:
getPostion -> pushBack to list -> setPosition
- exist task:
get from timers -> moveTask()
MoveTimer() -> moveTask()
From the above call chain, there is a function that will be called: moveTask()
The above process has the following situations:
delay <internal
: Because <single time precision, it means that this task has expired and needs to be executed immediately- For changed
delay
:new >= old
:<newPos, newCircle, diff>
newCircle> 0
: Calculate diff and convert circle to the next layer, so diff + numslots- If only the delay time is shortened, delete the old task mark, re-add it to the list, and wait for the next loop to be executed
#
ExecuteIn the previous initialization, the timer in run()
kept advancing, and the process of advancing was mainly to pass the tasks in the list to the executed execute func
. Let's start with the execution of the timer:
Next is how to execute execute
:
The specific branching situation is explained in the comments. When you look at it, it can be combined with the previous moveTask()
, where the circle
drops, and the calculation of diff
is the key to linking the two functions.
As for the calculation of diff
, the calculation of pos, circle
is involved:
The above process can be simplified to the following:
#
SummaryThe timingWheel
is driven by the timer. As the time advances, the tasks of the list
"doubly linked list" in the current time grid will be taken out and passed to the execute
for execution.
In terms of time separation, the time wheel has circle
layers, so that the original numSlots
can be reused continuously, because the timer is constantly loop
, and execution can drop the upper layer slot
to the lower layer. You can execute the task up to the upper level continuously in the loop
.
There are many useful component tools in go-zero
. Good use of tools is of great help to improve service performance and development efficiency. I hope this article can bring you some gains.