Java IO—HashedWheelTimer

UML Java IO—HashedWheelTimer
文章图片

时间轮算法 Java IO—HashedWheelTimer
文章图片

时间轮是一种高效、低延迟的调度数据结构。 其在Linux内核中广泛使用,是Linux内核定时器的实现方法和基础之一。 按使用场景,大致可以分为两种时间轮:原始时间轮和分层时间轮。 分层时间轮是原始时间轮的升级版本,来应对时间“槽”数量比较大的情况,对内存和精度都有很高要求的情况。 延迟任务的场景一般只需要用到原始时间轮就可以。

constructor
public HashedWheelTimer( ThreadFactory threadFactory, //定时任务都是后台任务,需要开启线程,我们通常会通过自定义 threadFactory 来命名线程,嫌麻烦就使用 Executors.defaultThreadFactory() long tickDuration, //一格的时间长度,默认100ms TimeUnit unit, //一格的时间长度,默认100ms int ticksPerWheel, //一圈有多少格,默认 512 boolean leakDetection,//用于追踪内存泄漏 long maxPendingTimeouts//最大允许等待的 Timeout 实例数,也就是我们可以设置不允许太多的任务等待。如果未执行任务数达到阈值,那么再次提交任务会抛出 RejectedExecutionException 异常。默认不限制 ) {}

原理分析 Java IO—HashedWheelTimer
文章图片

1、HashedWheelTimer本质上是一个Timer,用于将任务定时执行,newTimeout用于添加任务,stop用于终止Timer执行。
2、HashedWheelTimeout封装了待执行的任务TimerTask,并记录属于哪个时间轮,被添加到哪个bucket上,以及前后节点信息,并提供了任务取消、删除和超时执行的能力。
3、Worker封装了线程执行任务的能力。
4、HashedWheelBucket维护了存储待执行任务的双向链表,并提供了添加、删除和超时执行任务的能力。
使用场景 时间轮是一个高性能,低消耗的数据结构,它适合用非准实时,延迟的短平快任务,例如心跳检测。在netty和kafka中都有使用。
HashedWheelTimer本质是一种类似延迟任务队列的实现,那么它的特点如上所述,适用于对时效性不高的,可快速执行的,大量这样的“小”任务,能够做到高性能,低消耗。
HashedWheelTimer时间轮是一个高性能,低消耗的数据结构,它适合用非准实时,延迟的短平快任务,比如心跳检测和会话探活,对于可靠性要求比较严格的延迟任务,时间轮目前并不是比较好的解决方案,原因如下:原生时间轮是单机的,在分布式和多实例部署的场景中乏力;宕机重新恢复执行,原生时间轮的存储是Mpsc队列,毫无疑问是内存存储,如果出现宕机或者重启,数据是不可恢复的;对于类似订单超时取消的场景,可以考虑时间轮+zk + db的方式实现,zk做中心化控制,避免超时任务在多节点重复执行,也即是数据去重,db做为延时任务的持久化存储,宕机可恢复。
HashedWheelTimer应用场景大致有:
● 心跳检测(客户端探活)
● 会话、请求是否超时
● 消息延迟推送
● 业务场景超时取消(订单、退款单等)
参考文章 【Java IO—HashedWheelTimer】https://www.bianchengquan.com...
https://www.jianshu.com/p/1eb...
https://www.javadoop.com/post...

    推荐阅读