HashedTimerWheel(dubbo版)

写在前面

代码讲的少,就讲流程。流程懂了再自己去看代码就会看得很顺利;而且代码它细节比较多,干扰我讲流程,所以就不讲了。

一点点历史

据我所知,dubbo、caffeine、kafka、netty 都有用到哈希时间轮,我看的是 dubbo 的,其它的有什么区别我就先不看了,应该原理差不多

接口

哈希时间轮,三个接口 TimerTimeoutTimerTask,来看看这三个接口的方法:

// 要执行的任务。可以根据 timeout 判断还要不要执行
public interface TimerTask {
    void run(Timeout timeout) throws Exception;
}
// 一个控制信息,记录了一次任务的触发
// 它同时持有定时器 timer 和要执行的任务 task,还有几个查询方法
public interface Timeout {
    Timer timer();
    TimerTask task();
    boolean isExpired();
    boolean isCancelled();
    boolean cancel();
}
// 定时器对象。它可以添加一个在未来执行的任务,然后返回一个 timeout 对象,你可以利用这个 timout 对象对任务加以控制
public interface Timer {
    Timeout newTimeout(TimerTask task, long delay, TimeUnit unit);
    Set<Timeout> stop();
    boolean isStop();
}

看了这三个接口,大概可以知道什么意思:

  1. 我有这么一个 task,里面可以跑一些代码块,类似于 runnable;
  2. 然后我可以把这个 task 丢到 timer 里,设置超时时间,然后返回一个 timout;这个任务应该会在设置的时间到达后开始执行
  3. 丢进去后返回了一个 timeout 对象;我们可以判断是不是执行过了,还可以取消这个任务

一些特性

哈希时间轮,timer,只有一个工作线程,这决定了要执行的任务必须非常快,不然到时间的任务不能被准确执行了。

创建 timer 时,指定一个 ticks,意思是这个轮儿它一圈能 tick 几下。然后用的是类似 hashmap 的那种按位与的方式求得一个下标。

timer 有一个 bucket 数组,每个 bucket 里装了一个双向链表,里面全是 timeout;当时间 tick 到这个 bucket 时,里面的任务会按顺序全部执行

当提交一个 task 时,可以指定一个过期时间,如果超过了一圈的 tick,它有个 remainingRounds 字段,这个 remainingRounds 会记录你超过了几圈,类似钟表。如果大于一圈,每次到这个位置都会减一圈;如果剩 0 圈,说明这次可以触发了,就触发它

流程

  1. 跳一下指的是,worker sleep tick 那么长的时间
  2. 创建一个 timer,指定每圈跳几下、隔多久跳一下,还有一些其它的参数
  3. 不用显式启动,你提交第一个 task,就自动启动了;你要不提交任务,它一直不启动。提交的 task 会进到 timer 的任务队列里
  4. 在启动阶段:唯一的一个工人开始干活了。它不断跳一下,然后从把 timer 任务队列里的任务,放到算出来的桶下标里。然后把这个下标下面的任务(是个双向链表)挨个执行(跳过被取消的任务,of course)
    细心的朋友可能会问了:那你执行任务的过程中,有人又加了个任务,在你执行当前任务时,这个新增的任务的执行时间过去了,它会不会被漏执行?
    当然不会了(如果是我写的,当然会了,我会说这是用户的责任),因为他先放到了 timer 的队列里,然后工人 tick 的时候,才从 timer 队列转移到相应的 bucket 下面。如果算出来任务要执行的时间比当前要早,则往后顺延到即将被执行的 bucket 里
  5. 原理就这么多了,其它的内容我感觉没这些重要

几个例子,dubbo 的

因为我看的是 dubbo 的,就不提别的了。它里面有这些类实现了 TimerTask 接口:
AbstractTimerTask,这个东西的 run 方法每次执行完后会调用 reput,把任务重新放回 timer,也就形成了一个不断执行的这么一个效果;留了一个抽象方法 doTask,留给子类实现(模板设计模式,是你吗(模板模式:是我))。
子类有好几个,说一个吧!
CloseTimerTask
它的构造方法有一个 ChannelProvider,一个 tick,一个idleTimeout。第一个不用管;tick 就是多久检查一次超时;idleTimeout 就是多长时间没有读写请求后关闭 channel。
doTask 方法就是判断是不是上一次读或写到现在比 idleTimeout 长,是的话就关闭 channel

写在最后

感觉讲的不太好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值