欢迎关注方志朋的博客,回复”666“获面试宝典

来源:https://juejin.cn/post/7047405443961847816

前言

Kafka 中有很多延时操作,比如对于耗时的网络请求(比如 Produce 时等待 ISR 副本复制成功)会被封装成 DelayOperation 进行延迟处理操作,防止阻塞 Kafka请求处理线程。

Kafka 没有使用 JDK 自带的 Timer 和 DelayQueue 实现。因为时间复杂度上这两者插入和删除操作都是 O(logn),不能满足 Kafka 的高性能要求。

冷知识:JDK Timer 和 DelayQueue 底层都是个优先队列,即采用了 minHeap 的数据结构,最快需要执行的任务排在队列第一个,不一样的是 Timer 中有个线程去拉取任务执行,DelayQueue 其实就是个容器,需要配合其他线程工作。ScheduledThreadPoolExecutor 是 JDK 的定时任务实现的一种方式,其实也就是 DelayQueue + 池化线程的一个实现。

Kafka 基于时间轮实现了延时操作,时间轮算法的插入删除操作都是 O(1) 的时间复杂度,满足了 Kafka 对于性能的要求。除了 Kafka 以外,像 Netty 、ZooKeepr、Dubbo 这样的开源项目都有使用到时间轮的实现。

那么时间轮算法是怎么样的,算法思想是什么?Kafka 中又是怎么实现它的。

Kafka 时间轮算法

时间轮的算法思想可以通过我们日常生活中的钟表来理解。

Kafka 中的时间轮(TimingWheel)是一个存储定时任务的环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表(TimerTaskList)。TimerTaskList是一个环形的双向链表,链表中的每一项表示的都是定时任务项(TimerTaskEntry),其中封装了真正的定时任务(TimerTask)。

img

图中的几个参数:

  • tickMs: 时间跨度

  • wheelSize: 时间轮中 bucket 的个数

  • startMs: 开始时间

  • interval:时间轮的整体时间跨度 = tickMs * wheelSize

  • currentTime: tickMs 的整数倍,代表时间轮当前所处的时间

    • currentTime可以将整个时间轮划分为到期部分和未到期部分,currentTime当前指向的时间格也属于到期部分,表示刚好到期,需要处理此时间格所对应的TimerTaskList中的所有任务

整个时间轮的总体跨度是不变的,随着指针currentTime的不断推进,当前时间轮所能处理的时间段也在不断后移,总体时间范围在currentTime和currentTime+interval之间。

现在你可能会有疑问,这个抽象的 currentTime 怎么推进呢,别急看下文

那么如何支持大跨度的定时任务呢?

如果要支持几十万毫秒的定时任务,难不成要扩容时间轮的那个数组?实际上这里有两种解决方案:

  • 使用增加轮次/圈数的概念(Netty 的 HashedWheelTimer )

    • 举例来说,比如目前是 "0-7" 8个槽,41 % 8 + 1 = 2,即应该放在槽位是 2,下标是 1 的位置。然后 ( 41 - 1 ) / 8 = 5,即轮数记为 5。也就是说当循环 5 轮之后扫到下标的 1 的这个槽位会触发这个任务。

    • 具体实现细节这里不详述

  • 使用多层时间轮的概念 (Kafka 的 TimingWheel)

    • 相较于上个方案,层级时间轮能更好控制时间粒度,可以应对更加复杂的定时任务处理场景,适用的范围更广;

多层时间轮就更像我们钟表的概念了。秒针走的一圈、分针走的一圈和时针走的一圈就形成了一个多层时间轮的关系。

img

第N层时间轮走了一圈,等于 N+1 层时间轮走一格。即高一层时间轮的时间跨度等于当前时间轮的整体跨度。

在任务插入时,如果第一层时间轮不满足条件,就尝试插入到高一层的时间轮,以此类推。

随着时间推进,也会有一个时间轮降级的操作,原本延时较长的任务会从高一层时间轮重新提交到时间轮中,然后会被放在合适的低层次的时间轮当中等待处理;

在 Kafka 中时间轮之间如何关联呢,如何展现这种高一层的时间轮关系?

其实很简单就是一个内部对象的指针,指向自己高一层的时间轮对象。

另外还有一个问题,如何推进时间轮的前进,让时间轮的时间往前走。

  • Netty 中的时间轮是通过工作线程按照固定的时间间隔 tickDuration 推进的

    • 如果长时间没有到期任务,这种方案会带来空推进的问题,从而造成一定的性能损耗;

  • Kafka 则是通过 DelayQueue 来推进,是一种空间换时间的思想;

    • DelayQueue 中保存着所有的 TimerTaskList 对象,根据时间来排序,这样延时越小的任务排在越前面。

    • 外部通过一个线程(叫做ExpiredOperationReaper)从 DelayQueue 中获取超时的任务列表 TimerTaskList,然后根据 TimerTaskList 的 过期时间来精确推进时间轮的时间,这样就不会存在空推进的问题啦。

其实 Kafka 采用的是一种权衡的策略,把 DelayQueue 用在了合适的地方。DelayQueue 只存放了 TimerTaskList,并不是所有的 TimerTask,数量并不多,相比空推进带来的影响是利大于弊的

总结

  • Kafka 使用时间轮来实现延时队列,因为其底层是任务的添加和删除是基于链表实现的,是 O(1) 的时间复杂度,满足高性能的要求;

  • 对于时间跨度大的延时任务,Kafka 引入了层级时间轮,能更好控制时间粒度,可以应对更加复杂的定时任务处理场景;

  • 对于如何实现时间轮的推进和避免空推进影响性能,Kafka 采用空间换时间的思想,通过 DelayQueue 来推进时间轮,算是一个经典的 trade off。

热门内容:
  • 干掉visio,这个画图神器真的绝了!!!

  • 面试官问:生成订单30分钟未支付,则自动取消,该怎么实现?

  • 不是我吹,这个idea插件你真没用过!

  • 比 Xshell 还好用的 SSH 客户端神器,MobaXterm 太爱了!

  • RedisJson 横空出世,性能碾压ES和Mongo!

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

从 Kafka 看时间轮算法设计相关推荐

  1. 孙玄辜教授:基于Linux内核的时间轮算法设计实现【附代码】

    文章目录 1.时间轮算法基本思想 2.定时器的添加 3.定时器到期处理 孙玄:毕业于浙江大学,现任转转公司首席架构师,技术委员会主席,大中后台技术负责人(交易平台.基础服务.智能客服.基础架构.智能运 ...

  2. 面试官:知道时间轮算法吗?在Netty和Kafka中如何应用的?

    最近看 Kafka 看到了时间轮算法,记得以前看 Netty 也看到过这玩意,没太过关注.今天就来看看时间轮到底是什么东西. 为什么要用时间轮算法来实现延迟操作? 延时操作 Java 不是提供了 Ti ...

  3. 小顶堆时间复杂度_时间轮算法以及时间轮在Netty和Kafka中的应用的

    大家好,我是yes. 最近看 Kafka 看到了时间轮算法,记得以前看 Netty 也看到过这玩意,没太过关注.今天就来看看时间轮到底是什么东西. 为什么要用时间轮算法来实现延迟操作? 延时操作 Ja ...

  4. 6张图阐述Kafka心跳机制(时间轮算法的具体运用)

    Broker端与客户端的心跳在Kafka中非常的重要,因为一旦在一个心跳过期周期内(默认10s),Broker端的消费组组协调器(GroupCoordinator)会把消费者从消费组中移除,从而触发重 ...

  5. 原 荐 简单说说Kafka中的时间轮算法

    零.时间轮定义简单说说时间轮吧,它是一个高效的延时队列,或者说定时器.实际上现在网上对于时间轮算法的解释很多,定义也很全,这里引用一下 朱小厮博客 里出现的定义:参考下图,Kafka中的时间轮(Tim ...

  6. 心跳超时时间设置_定时器实现之时间轮算法

    前言 在看这篇文章的时候对其中超时控制一块儿有点好奇.通过时间轮来控制超时?啥是时间轮?怎么控制的?文章会先介绍常见的计时超时处理,再引入时间轮介绍及 netty 在实现时的一些细节,最后总结下实现的 ...

  7. java 时间轮算法_时间轮算法(TimingWheel)是如何实现的?

    前言 我在2. SOFAJRaft源码分析-JRaft的定时任务调度器是怎么做的?这篇文章里已经讲解过时间轮算法在JRaft中是怎么应用的,但是我感觉我并没有讲解清楚这个东西,导致看了这篇文章依然和没 ...

  8. 时间轮算法(TimingWheel)

    时间轮算法的应用非常广泛,在 Dubbo.Netty.Kafka.ZooKeeper.Quartz 的组件中都有时间轮思想的应用,甚至在 Linux 内核中都有用到. 其思想理论基础可以参考论文:ht ...

  9. 时间轮算法解析(Netty HashedWheelTimer源码解读)

    1.背景 时间轮算法可以用于高效的执行大量的定时任务. 在Netty中的一个典型应用场景是判断某个连接是否idle,如果idle(如客户端由于网络原因导致到服务器的心跳无法送达),则服务器会主动断开连 ...

最新文章

  1. 【转】Silverlight 3 Beta 新特性解析(7)- Child Window和Shader Effect篇
  2. 对Ubuntu操作系统进行彻底优化
  3. html图片墙 无限滚动,尝试用CSS3实现无限循环的无缝滚动
  4. (提示)ubuntu16.04通过sealos安装k8s,需要重新部署apply一下calico组件
  5. Java工程师知识图谱
  6. java 上传源码_和付费网盘说再见,跟着本文自己起个网盘(Java)
  7. NOIP模拟测试49·50「养花·折射·画作·施工·蔬菜·联盟」
  8. Linux小宝典之理解Chroot模式
  9. [转载]MongoDB的$inc修改器
  10. 纯CSS实现鼠标滑过缩略图放大显示代码
  11. baseresponse响应类_SpringBoot统一响应体解决方案
  12. 经济学原理_宏观经济学,微观经济学合集 N.格里高利·曼昆PDF
  13. I350系列服务器网卡驱动,Intel英特尔网卡驱动
  14. linux ubuntu 加密狗,ubuntu – 将usb加密狗连接到KVM VM
  15. codeblocks编译器报错Id returned 1 exit status
  16. 博弈论战略式表述和扩展式表述
  17. win2008服务器系统玩红警,Win10系统玩不了红警2怎么办?
  18. 赛尔号对战c语言程序,赛尔号计算解析系列文章(第一期)
  19. 2019计算机小高考成绩,小高考没过怎么办 2021小高考难度如何
  20. pycharm环境配置问题解决一览

热门文章

  1. SpringBoot最新面试题及答案,汇总版
  2. 如何发表一篇好的文章
  3. Python中的小细节
  4. ReentrantLock实现原理分析
  5. Spring Boot配置全局异常捕获
  6. Jmeter(二十九)_dotnet搭建本地接口服务
  7. python统计列表内元素个数
  8. JS 实现可停顿的垂直滚动
  9. 每天写的叫工作日志,每周写的总结叫周报,每月写的叫月报
  10. window 下相关命令