1

1. Introduction to the Time Wheel Algorithm

In order for everyone to understand the code below, let's first briefly understand the core principle of the netty time wheel algorithm

The time wheel algorithm lives up to its name. The time wheel is a circular data structure, similar to a dial, which divides the time wheel into multiple buckets (for example: 0-8). Assuming that the separation time period of each time wheel is tickDuration=1s (that is, the time it takes for the pointer to pass through each grid is 1 s), and the current time bucket=3, then the tasks that need to be executed after 18 seconds need to fall to ( (3+18)%8=5 on the No. 5 bucket. If there are multiple tasks that need to be executed during this time period, a doubly linked list will be formed.
In addition, for the time wheel, we must have the following understandings:

  • The time wheel pointer is a worker thread that executes the tasks in the doubly linked list at the hour of the time wheel.
  • The time wheel algorithm is not an accurate delay, its execution accuracy depends on the separation time period tickDuration of each time wheel piece
  • The worker thread is a single thread, one bucket, one bucket to process tasks sequentially. Therefore, our delayed task must be an asynchronous task, otherwise it will affect the execution time of the subsequent tasks of the time wheel.

2. Time wheel hello-world

To implement an example of a delayed task, the requirements are still very simple: you buy a train ticket, you must pay within 30 minutes, otherwise the order will be automatically canceled. If the order is not paid for within 30 minutes, it will be automatically cancelled. This task is a delayed task. Our train ticket order cancellation task does not require a very accurate delay in terms of demand, so the time wheel algorithm can be used to complete this task.

First introduce netty through maven coordinates

 <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.45.Final</version>
</dependency>

Then we create a time wheel, which we can do if it's a Spring development environment. In the following, we create a new time wheel containing 512 buckets, and the time interval of each time wheel is 100 milliseconds.

 @Bean("hashedWheelTimer")
public HashedWheelTimer hashedWheelTimer(){
    return new HashedWheelTimer(100, TimeUnit.MILLISECONDS, 512);
}

Example: When a user buys a train ticket and places an order, add a 30-minute delay task to the time wheel. The delayed task will be executed after 30 minutes. The lambda expression section below implements a TimerTask(task) delayed task. In the function body of this delayed task, please use an asynchronous task, that is: start a separate thread or use the SpringBoot asynchronous task thread pool. Because the worker thread is single-threaded, your task processing time longer than tickDuration will prevent the execution of tasks on subsequent time wheels.

 //订单下单操作
void order(String orderInfo) {
  //下单的时候,向时间轮中添加一个30分钟的延时任务
  hashedWheelTimer.newTimeout(task -> {
    //注意这里使用异步任务线程池或者开启线程进行订单取消任务的处理
    cancelOrder(orderInfo);
  }, 30, TimeUnit.MINUTES);
}

3. Asynchronous task thread pool

We have emphasized many times in the above that the execution content of the task TimerTask of the time wheel should be asynchronous. The simplest way is to start a thread to process the task after receiving a task. In the Spring environment, we actually have a better choice, which is to use Spring's thread pool, which can be customized. For example: the usage below is that I define a thread pool named test in advance, and then use it through @Async.

 @Async("test")
public void cancelOrder(String orderInfo){
  //查询订单支付信息,如果用户未支付,关闭订单
}

There may be some friends who don't know how to customize a Spring thread pool. You can refer to: I wrote an open source project of SpringBoot's observable and easy-to-configure thread pool before, source code address: https://gitee.com/ hanxt/zimug-monitor-threadpool . My zimug-monitor-threadpool open source project can monitor the usage of the thread pool. I usually use it with good results. I recommend it to everyone!

Fourth, the advantages and disadvantages of the time wheel

The advantage of the time wheel algorithm for implementing delayed tasks is that, compared with the DelayQueue using JDK, it has advantages in algorithm and relatively better execution performance.
The disadvantage is that all delayed tasks and delay-triggered management are performed in the memory of a single application service. Once the application service fails and restarts the service, all the time wheel task data will be lost. This disadvantage is the same as DelayQueue. In order to solve this problem, we can use distributed middleware such as redis and RocketMQ to manage delayed task messages to implement delayed tasks, which I will introduce to you in subsequent articles.
Welcome to pay attention to my announcement number: Antetokounmpo, reply 003 and present the PDF version of the author's column "The Way of Docker Cultivation", more than 30 high-quality docker articles. Antetokounmpo Blog: zimug.com


字母哥博客
933 声望1.5k 粉丝