Kafka时间轮详解

今天和大家聊下Kafka的时间轮实现原理

Kafka时间轮的底层就是一个环形数组,而数组中每个元素都存放一个双向链表TimerTaskList,链表中封装了很多延时任务。

Kafka中一个时间轮TimingWheel是由20个时间格组成,wheelSize = 20;每格的时间跨度是1ms,tickMs = 1ms。参照Kafka,上图中也用了20个灰边小圆表示时间格,为了动画演示可以看得清楚,我们这里每个小圆的时间跨度是1s。

所以现在整个时间轮的时间跨度就是 tickMs * wheelSize ,也就是 20s。从0s到19s,我们都分别有一个灰边小圆来承载。

Kafka的时间轮还有一个表盘指针 currentTime,表示时间轮当前所处的时间。也就是图中用黑色粗线表示的圆,随着时间推移, 这个指针也会不断前进;
在这里插入图片描述
有了时间轮,现在可以往里面添加定时任务了。我们用一个粉红色的小圆来表示一个定时任务。
在这里插入图片描述

初始的时候, 时间轮的指针定格在0。此时添加一个超时时间为2s的任务, 那么这个任务将会插入到第二个时间格中。在这里插入图片描述
当时间轮的指针到达第二个时间格时, 会处理该时间格上对应的任务。在动画上就是让红色的小圆消失!
在这里插入图片描述
如果这个时候又插入一个延时时间为8s的任务进来, 这个任务的过期时间就是在当前时间2s的基础上加8s, 也就是10s, 那么这个任务将会插入到过期时间为10s的时间格中。
在这里插入图片描述

2
"动态"时间轮
到目前为止,一切都很好理解。
那么如果在当前时间是2s的时候, 插入一个延时时间为19s的任务时,这个任务的过期时间就是在当前时间2s的基础上加19s, 也就是21s。
请看下图,当前的时间轮是没有过期时间为21s的时间格。这个任务将会插入到过期时间为1s的时间格中,这是怎么回事呢?
在这里插入图片描述
复用时间格
为了解答上面的问题,我们先来点魔法, 让时间轮上的时间都动起来!
在这里插入图片描述
  其实呢,当指针定格在2s的位置时, 时间格0s, 1s和2s就已经是过期的时间格。
  也就是说指针可以用来划分过期的时间格[0,2]和未来的时间格 [3,19]。而过期的时间格可以继续复用。比如过期的时间格0s就变成了20s, 存放过期时间为20s的任务。
  理解了时间格的复用之后,再看回刚刚的例子,当前时间是2s时,添加延时时间为19s的任务,那么这个任务就会插入到过期时间为21s的时间格中。
在这里插入图片描述

3
  时间轮升级
  下面,新的问题来了,请坐好扶稳。
  如果在当前时间是2s的时候, 插入一个延时时间为22s的任务, 这个任务的过期时间就是在2s的基础上加22s,也就是24s。
在这里插入图片描述
显然当前时间轮是无法找到过期时间格为24秒的时间格,因为当前过期时间最大的时间格才到21s。而且我们也没办法像前面那样再复用时间格,因为除了过期时间为2s的时间格,其他的时间格都还没过期呢。当前时间轮无法承载这个定时任务,那么应该怎么办呢?
  当然我们可以选择扩展时间轮上的时间格, 但是这样一来,时间轮就失去了意义。
  是时候要升级时间轮了!
  我们先来理解下多层时间轮之间的联系。

4
  层级时间轮
  如图是一个两层的时间轮:
在这里插入图片描述
  第二层时间轮也是由20个时间格组成, 每个时间格的跨度是20s。
  图中展示了每个时间格对应的过期时间范围, 我们可以清晰地看到, 第二层时间轮的第0个时间格的过期时间范围是 [0,19]。也就是说, 第二层时间轮的一个时间格就可以表示第一层时间轮的所有(20个)时间格;
  为了进一步理清第一层时间轮和第二层时间轮的关系, 我们拉着时间的小手, 一起观看下面的动图:
在这里插入图片描述
  可以看到,第二层时间轮同样也有自己的指针, 每当第一层时间轮走完一个周期,第二层时间轮的指针就会推进一格。
  添加定时任务
  回到一开始的问题,在当前时间是2s的时候, 插入一个延时时间为22s的任务,该任务过期时间为24s。
在这里插入图片描述
  当第一层时间轮容纳不下时,进入第二层时间轮,并插入到过期时间为[20,39]的时间格中。
  我们再来个例子,如果在当前时间是2s的时候, 插入一个延时时间为350s的任务, 这个任务的过期时间就是在2s的基础上加350s,也就是352s。
在这里插入图片描述
从图中可以看到,该任务插入到第二层时间轮过期时间为[340,359]s的时间格中,也就是第17格的位置。

5
  "动态"层级时间轮
  通常来说, 第二层时间轮的第0个时间格是用来表示第一层时间轮的, 这一格是存放不了任务的, 因为超时时间0-20s的任务, 第一层时间轮就可以处理了。
  但是! 事情往往没这么简单, 我们时间轮上的时间格都是可以复用的! 那么这在第二层时间轮上又是怎么体现的呢?
  下面是魔法时间, 我们让时间轮上的过期时间都动起来!
在这里插入图片描述
  从图中可以看到,当第一层时间轮的指针定格在1s时,超时时间0s的时间格就过期了。而这个时候,第二层时间轮第0个时间格的时间范围就从[0,19]分为了过期的[0],和未过期的[1,19]。而过期的[0]就会被新的过期时间[400]复用。
  第二层时间轮第0个时间格的过期时间范围演变如下:
  [0-19]
  [400][1,19]
  [400,401][2,19]
  …
  [400,419]
  所以,如果在当前时间是2s的时候, 插入一个延时时间为399s的任务, 这个任务的过期时间就是在2s的基础上加399s,也就是401s。如图,这个任务还是会插到第二层时间轮第0个时间格中去。
在这里插入图片描述

6
  时间轮降级
  还是用回这个大家都已经耳熟能详的例子,在当前时间是2s的时候, 插入一个延时时间为22s的任务,该任务过期时间为24s。最后进入第二层时间轮,并插入到过期时间为[20,39]的时间格中。
  当二层时间轮上的定时任务到期后,时间轮是怎么做的呢?
在这里插入图片描述
  从图中可以看到,随着当前时间从2s继续往前推进,一直到20s的时候,总共经过了18s。此时第二层时间轮中,超时时间为[20-39s]的时间格上的任务到期。
  原本超时时间为24s的任务会被取出来,重新加入时间轮。此时该定时任务的延时时间从原本的22s,到现在还剩下4s(22s-18s)。最后停留在第一层时间轮超时时间为24s的时间格,也就是第4个时间格。
  随着当前时间继续推进,再经过4s后,该定时任务到期被执行。
  从这里可以看出时间轮的巧妙之处,两层时间轮只用了40个数组元素,却可以承载[0-399s]的定时任务。而三层时间轮用60个数组元素,就可以承载[0-7999s]的定时任务!
在这里插入图片描述

引用知乎大佬热爱生活的小胖胖秒懂 Kafka 时间轮(TimingWheel)文章,讲解的浅显易懂,强烈推荐!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值