共计 1795 个字符,预计需要花费 5 分钟才能阅读完成。
一、工夫轮算法简介
为了大家可能了解下文中的代码,咱们先来简略理解一下 netty 工夫轮算法的外围原理
工夫轮算法货真价实,工夫轮就是一个环形的数据结构,相似于表盘,将工夫轮分成多个 bucket(比方:0-8)。假如每个工夫轮轮片的分隔时间段 tickDuration=1s(即:指针通过每个格子破费工夫是 1 s),以后的工夫 bucket=3,那么在 18 秒后须要被执行的工作须要落到((3+18)%8= 5 取余运算)的 5 号 bucket 上。如果有多个须要在该时间段内执行的工作,就会组成一个双向链表。
另外针对工夫轮咱们要有上面的几个认知:
- 工夫轮指针是一个 Worker 线程,在工夫轮整点的时候执行双向链表中的工作。
- 工夫轮算法的并不是精准的延时,它的执行精度取决于每个工夫轮轮片的分隔时间段 tickDuration
- Worker 线程是单线程,一个 bucket、一个 bucket 的程序解决工作。所以咱们的延时工作肯定要做成异步工作,否则会影响工夫轮后续工作的执行工夫。
二、工夫轮 hello-world
实现一个延时工作的例子,需要依然非常的简略:你买了一张火车票,必须在 30 分钟之内付款,否则该订单被主动勾销。订单 30 分钟不付款主动勾销,这个工作就是一个延时工作。咱们的火车票订单勾销工作,从需要上看并不需要十分精准的延时,所以是能够应用工夫轮算法来实现这个工作的。
首先通过 maven 坐标引入 netty
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.45.Final</version>
</dependency>
而后咱们创立一个工夫轮,如果是 Spring 的开发环境,咱们能够这么做。下文中咱们 new 了一个蕴含 512 个 bucket 的工夫轮,每个工夫轮的轮片工夫距离是 100 毫秒。
@Bean("hashedWheelTimer")
public HashedWheelTimer hashedWheelTimer(){return new HashedWheelTimer(100, TimeUnit.MILLISECONDS, 512);
}
举例:当用户买火车票下单的时候,向工夫轮中增加一个 30 分钟的延时工作。延时工作将在 30 分钟之后被执行,下文的 lambda 表达式局部实现了一个 TimerTask(task) 延时工作。这个延时工作的函数体内,请肯定应用异步工作,即:独自起一个线程或者应用 SpringBoot 异步工作线程池。因为 Worker 线程是单线程的,你的工作解决工夫长于 tickDuration 会障碍后续工夫轮轮片上的工作的执行。
// 订单下单操作
void order(String orderInfo) {
// 下单的时候,向工夫轮中增加一个 30 分钟的延时工作
hashedWheelTimer.newTimeout(task -> {
// 留神这里应用异步工作线程池或者开启线程进行订单勾销工作的解决
cancelOrder(orderInfo);
}, 30, TimeUnit.MINUTES);
}
三、异步工作线程池
咱们在上文中曾经屡次强调,工夫轮的工作 TimerTask 的执行内容要做成异步的。最简略的做法就是接到一个工作之后启动一个线程解决该工作。在 Spring 环境下其实咱们有更好的抉择,就是应用 Spring 的线程池,这个线程池是能够自定义的。比方:下文中的用法是我当时定义了一个名字为 test 的线程池,而后通过 @Async 应用即可。
@Async("test")
public void cancelOrder(String orderInfo){// 查问订单领取信息,如果用户未领取,敞开订单}
四、工夫轮优缺点
工夫轮算法实现延时工作的长处就是,绝对于应用 JDK 的 DelayQueue,其算法上具备劣势,执行性能绝对好一些。
其毛病就是所有的延时工作以及延时触发的治理,都是在单个应用服务的内存中进行的,一旦该应用服务产生故障重启服务,工夫轮工作数据将全副失落。这一毛病和 DelayQueue 是一样的。为了解决这个问题,咱们能够应用 redis、RocketMQ 等分布式中间件来治理延时工作音讯的形式来实现延时工作,这个我会在后续的文章中为大家介绍。
对文章中内容感兴趣的小伙伴能够搜寻微信公众号:敲代码的老贾,支付相应材料