Kafka 深入服务端 — 时间轮
Kafka中存在大量的延迟操作,比如延时生产、延时拉取和延时删除等。Kafka基于时间轮概念自定义实现了一个用于延时功能的定时器,来完成这些延迟操作。
1 时间轮
Kafka没有使用基于JDK自带的Timer或DelayQueue来实现延迟功能,因为它们的插入和删除操作的时间复杂度为logn,这不能满足Kafka高性能要求。
1.1 Timer 和 DelayQueue
它们都使用了一个优先级队列(通常基于堆实现)来管理任务。
1.1.1 Timer
用于计划在特定时间后执行的任务,这些任务可以只执行一次或定期重复执行。其有以下特点:
- 运行在单线程中,无法满足多个任务同时执行的需求。如果前置任务耗时长,可能会阻塞后置任务。
- 如果任务执行过程中抛出异常,Timer会被异常中断停止。
public class TimerTest {public static void main(String[] args) {Timer timer = new Timer();Date startTime = new Date();TimerTask task1 = new TimerTask() {@Overridepublic void run() {long dis = (new Date().getTime() - startTime.getTime()) / 1_000;System.out.println("task1执行,距离开始时间:" + dis + "s");}};TimerTask task2 = new TimerTask() {@Overridepublic void run() {long dis = (new Date().getTime() - startTime.getTime()) / 1_000;System.out.println("task2执行,距离开始时间:" + dis + "s,休眠5s。");try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("task2休眠结束");}};TimerTask task3 = new TimerTask() {@Overridepublic void run() {long dis = (new Date().getTime() - startTime.getTime()) / 1_000;System.out.println("task3执行,距离开始时间:" + dis + "s");}};timer.schedule(task1,1000); // 1s后执行timer.schedule(task2,2000); // 2s后执行timer.schedule(task3,3000); // 3s后执行}
// 执行结果:
// task1执行,距离开始时间:1s
// task2执行,距离开始时间:2s,休眠5s。
// task2休眠结束
// task3执行,距离开始时间:7s
}
1.1.2 DelayQueue
是一个无界阻塞队列,用于存储实现了Delayed接口的元素,这些元素只有在它们的延迟期满时才会被取出。其有以下特点:
- 线程安全,可以在多线程环境中使用。
- 无界队列,可以存储任意数量的元素,直到系统内存耗尽。
- 延迟精度依赖与系统时钟。
public class DelayQueueTest {private static class DelayQueueTask implements Delayed {private final String taskName;private final long delayTime;private DelayQueueTask(String taskName, long delayTime) {this.taskName = taskName;this.delayTime = delayTime + System.currentTimeMillis();}@Overridepublic long getDelay(TimeUnit unit) { // 返回剩余延迟long diff = delayTime - System.currentTimeMillis();return unit.convert(diff,TimeUnit.MILLISECONDS);}@Overridepublic int compareTo(Delayed o) {if (this.delayTime < ((DelayQueueTask) o).delayTime) {return -1;}if (this.delayTime > ((DelayQueueTask) o).delayTime) {return 1;}return 0;}@Overridepublic String toString() {return "DelayQueueTask{" +"taskName='" + taskName + '\'' +", delayTime=" + delayTime +'}';}}public static void main(String[] args) throws InterruptedException {DelayQueue<DelayQueueTask> delayQueue = new DelayQueue<>();delayQueue.offer(new DelayQueueTask("task1",5000));delayQueue.offer(new DelayQueueTask("task2",2000));delayQueue.offer(new DelayQueueTask("task3",4000));System.out.println("开始执行delayQueue任务");while (!delayQueue.isEmpty()) {DelayQueueTask task = delayQueue.take();System.out.println("任务:" + task);}System.out.println("delayQueue任务 任务执行完毕");}
}
1.2 时间轮结构
Kafka 在任务的插入与删除采用了时间轮结构,其时间复杂度为O(1),而在时间推进上,还是依赖JDK提供的DelayQueue。

图 时间轮(TimingWheel)结构
Kafka的时间轮是一个存储定时任务的环形队列,每个元素(时间格)相当于一个桶(bucket),来存储一个定时任务列表TimerTaskList。TimerTaskList是一个环形的双向链表,链表中的每一项都是定时任务项TimerTaskEntry,其中封装了真正的定时任务TimerTask。
Kafka将TimerTaskList插入到DelayQueue(队列)中,使其成为其中的一个元素。它的过期时间为TimerTaskList的TimerTaskEntry中最快过期的时间。
1.2.1 时间格与时间跨度
时间轮由多个时间格组成(上面示意图中,每一层有10个时间格),每个时间格代表时间轮的基本时间跨度(tick),时间格数(wheelSize)是固定的,那么时间轮的总体跨度interval = tick * wheelSize。
时间轮还有一个表盘指针(currentTime),用来表示时间轮当前所处的时间。currentTime是tick的整数倍,currentTime将整个时间轮划分为到期部分和未到期部分。当前指向的时间格也属于到期部分,此时需要处理此时间格所对应的TimerTaskList中的所有任务。
上面示意图中,tick = 1s,此时currentTime指向第2个时间格,需要处理这个时间格存储的所有任务。假设,此时插入了一个3s后的任务,则把该任务插入第5个时间格中的bucket。
1.2.2 时间轮层级
当currentTime 指向第2个时间格时,需要插入一个33s后的任务,此时时间超过了第一层的跨度(1s * 10 = 10s)。Kafka引入层级时间轮的概念,当到期时间超过了当前时间轮所表示的时间范围时,就会尝试添加到上层的时间轮中。
33s 的任务会被插入到第二层的第(33 / 10 = 3) 3 个时间格中。
第一层的开始时间(第0格)startMs 是当前系统时间。其余高层时间轮的起始时间都设置为创建此层时前面第一轮的currentTime。
每一层时间轮都会有指向更高一层的引用。
1.2.3 任务处理及时间轮降级

图 时钟
时间轮类似于时钟,当每一层走完一圈时,上一层就会走一格。例如当第1层的currentTime 指向第2格时,此时需要插入两个任务,分别是33s及39s后。它们都会被插入到第2层的第3格中的bucket(TimerTaskList)。
假设经过33s(第1层指向第5格,第2层指向第3格)后,此bucket还是只有这两个任务。Kafka会把它们所在的TimerTaskList从第2层的第3格中取出,将33s的任务执行并从TimerTaskList中删除。此时,39s的任务还剩6s,Kafka会把这个任务“降级”,插入到第1层第1((5+6)% 10)格中。
1.2.4 时间推进与DelayQueue
如果按照时间格一格格推进时间,这样消耗会比较大,而且可能好多时间格没有存储任务。Kafka借助DelayQueue来推进时间。
将时间格bucket的TimerTaskList封装成Delayed,其剩余时间取TimerTaskList中TimerTaskEntry最快达到的时间。然后将这些Delayed插入到DelayQueue中。DelayQueue会将这些Delayed排序,最快到达的排在队列头部。当到达时刻时,将表头的TimerTaskEntry取出,对它的TaskEntry执行任务执行或降级等操作。
相关文章:
Kafka 深入服务端 — 时间轮
Kafka中存在大量的延迟操作,比如延时生产、延时拉取和延时删除等。Kafka基于时间轮概念自定义实现了一个用于延时功能的定时器,来完成这些延迟操作。 1 时间轮 Kafka没有使用基于JDK自带的Timer或DelayQueue来实现延迟功能,因为它们的插入和…...
一文掌握ADB的安装及使用
文章目录 一、什么是ADB?二、 安装ADB2.1 下载ADB2.2 配置环境变量 三、连接Android设备四、 常用ADB命令五、ADB高级功能5.1 屏幕截图和录制5.2 模拟按键输入5.3 文件管理5.4 系统设置管理5.5 系统操作指令5.6 日志操作指令5.7 APK操作指令5.8 设备重启和恢复 六、…...
Linux系统下速通stm32的clion开发环境配置
陆陆续续搞这个已经很久了。 因为自己新电脑是linux系统无法使用keil,一开始想使用vscode里的eide但感觉不太好用;后面想直接使用cudeide但又不想妥协,想趁着这个机会把linux上的其他单片机开发配置也搞明白;而且非常想搞懂cmake…...
Java 9模块开发:IntelliJ IDEA实战指南
在Java 9中,模块化是一个重要的特性,它可以帮助我们更好地组织和管理代码。而IntelliJ IDEA作为一个强大的集成开发环境,为Java 9模块的开发提供了全面的支持。本文将通过一个实际的项目示例,详细讲解如何在IntelliJ IDEA中开发和…...
OpenCSG月度更新2025.1
1月的OpenCSG取得了一些亮眼的成绩 在2025年1月,OpenCSG在产品和社区方面继续取得了显著进展。产品方面,推出了AutoHub浏览器自动化助手,帮助用户提升浏览体验;CSGHub企业版功能全面升级,现已开放试用申请,…...
【算法与数据结构】动态规划
目录 基本概念 最长递增子序列(中等) 最大子数组和(中等) 基本概念 重叠子问题 一个问题可以被分解为多个子问题,并且这些子问题在求解过程中会被多次重复计算。例如,在计算斐波那契数列时,…...
AWTK 骨骼动画控件发布
Spine 是一款广泛使用的 2D 骨骼动画工具,专为游戏开发和动态图形设计设计。它通过基于骨骼的动画系统,帮助开发者创建流畅、高效的角色动画。本项目是基于 Spine 实现的 AWTK 骨骼动画控件。 代码:https://gitee.com/zlgopen/awtk-widget-s…...
【llm对话系统】什么是 LLM?大语言模型新手入门指南
什么是 LLM?大语言模型新手入门指南 大家好!欢迎来到 LLM 的奇妙世界!如果你对人工智能 (AI) 的最新进展,特别是那些能像人类一样阅读、写作甚至进行对话的 AI 感兴趣,那么你来对地方了。这篇文章将带你认识 LLM 的基…...
三角形的最大周长(LeetCode 976)
给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。如果不能形成任何面积不为零的三角形,返回 0。 示例 1: 输入:[2,1,2] 输出:5 示例 2&…...
go到底是什么意思:对go的猜测或断言
go这个单词,简单地讲,表示“走或去”的意思: go v.去;走 认真想想,go是一个非常神秘的单词,g-和o-这两个字母,为什么就会表达“去;走”的意思呢?它的字面义或本质&…...
学习数据结构(2)空间复杂度+顺序表
1.空间复杂度 (1)概念 空间复杂度也是一个数学表达式,表示一个算法在运行过程中根据算法的需要额外临时开辟的空间。 空间复杂度不是指程序占用了多少bytes的空间,因为常规情况每个对象大小差异不会很大,所以空间复杂…...
DeepSeek--通向通用人工智能的深度探索者
一、词源与全称 “DeepSeek"由"Deep”(深度)与"Seek"(探索)组合而成,中文译名为"深度求索"。其全称为"深度求索人工智能基础技术研究有限公司",英文对应"De…...
Unity游戏(Assault空对地打击)开发(1) 创建项目和选择插件
目录 前言 创建项目 插件导入 地形插件 前言 这是游戏开发第一篇,进行开发准备。 创作不易,欢迎支持。 我的编辑器布局是【Tall】,建议调整为该布局,如下。 创建项目 首先创建一个项目,过程略,名字请勿…...
(三)Session和Cookie讲解
目录 一、前备知识点 (1)静态网页 (2)动态网页 (3)无状态HTTP 二、Session和Cookie 三、Session 四、Cookie (1)维持过程 (2)结构 正式开始说 Sessi…...
【信息系统项目管理师-选择真题】2011下半年综合知识答案和详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9~10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第21题】…...
1.Template Method 模式
模式定义 定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类中。Template Method 使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特…...
【PyTorch】5.张量索引操作
目录 1. 简单行、列索引 2. 列表索引 3. 范围索引 4. 布尔索引 5. 多维索引 个人主页:Icomi 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活的平台。神经网络作为…...
力扣25.k个一组翻转链表
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。你不能只是单纯的改变节点内部的值&…...
[EAI-023] FAST: Efficient Action Tokenization for Vision-Language-Action Models
Paper Card 论文标题:FAST: Efficient Action Tokenization for Vision-Language-Action Models 论文作者:Karl Pertsch, Kyle Stachowicz, Brian Ichter, Danny Driess, Suraj Nair, Quan Vuong, Oier Mees, Chelsea Finn, Sergey Levine 论文链接&…...
2025年AI手机集中上市,三星Galaxy S25系列上市
2025年被认为是AI手机集中爆发的一年,各大厂商都会推出搭载人工智能的智能手机。三星Galaxy S25系列全球上市了。 三星Galaxy S25系列包含S25、S25和S25 Ultra三款机型,起售价为800美元(约合人民币5800元)。全系搭载骁龙8 Elite芯…...
八股文 (一)
文章目录 项目地址一、前端1.1 大文件上传,预览1.2 首页性能优化1.2 流量染色,灰度发布1.3 Websock心跳机制,大数据实时数据优化1.4 Gpu 加速 fps优化1.5 echarts包大小优化和组件封装1.6 前端监控系统1.7 超大虚拟列表卡顿1. 实现2. 相关问题(1) 什么是虚拟化列表,为什么要…...
在虚拟机里运行frida-server以实现对虚拟机目标软件的监测和修改参数(一)(android Google Api 35高版本版)
frida-server下载路径 我这里选择较高版本的frida-server-16.6.6-android-x86_64 以root身份启动adb 或 直接在android studio中打开 adb root 如果使用android studio打开的话,最好选择google api的虚拟机,默认以root模式开启 跳转到下载的frida-se…...
FLTK - FLTK1.4.1 - demo - animgifimage-play
文章目录 FLTK - FLTK1.4.1 - demo - animgifimage-play概述笔记END FLTK - FLTK1.4.1 - demo - animgifimage-play 概述 看的官方demo越多,在每个新demo中能看到的新增知识点越少。这是好事。 不可能一次将细节都记住,只要知道每个官方demo能干啥&…...
2024年除夕
多少年前的除夕,一如今天这样的除夕;多少年后的除夕,也一如多少年前的除夕。 无数个这样的除夕下午,我打开电脑,望着窗外安静的小区,车声渐渐稀疏的马路,想写下一些新的感受时,多少…...
如何实现滑动删除功能
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了GestureDetector Widget相关的内容,本章回中将介绍Dismissible Widget.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在这里介绍的Dismissible是一个事件响应Widget,它和GestureDetector类…...
golang通过AutoMigrate方法自动创建table详解
一.AutoMigrate介绍 1.介绍 在 Go 语言中,GORM支持Migration特性,支持根据Go Struct结构自动生成对应的表结构,使用 GORM ORM 库的 AutoMigrate 方法可以自动创建数据库表,确保数据库结构与定义的模型结构一致。AutoMigrate 方法非常方便&am…...
JAVA:利用 Content Negotiation 实现多样式响应格式的技术指南
1、简述 Content Negotiation(内容协商) 是 RESTful 服务的重要特性,允许客户端和服务器根据请求的不同特性动态选择适合的响应格式。它是一种在 HTTP 协议中实现的机制,通过它,服务器能够根据客户端需求返回适合的内…...
Python 函数魔法书:基础、范例、避坑、测验与项目实战
Python 函数魔法书:基础、范例、避坑、测验与项目实战 内容简介 本系列文章是为 Python3 学习者精心设计的一套全面、实用的学习指南,旨在帮助读者从基础入门到项目实战,全面提升编程能力。文章结构由 5 个版块组成,内容层层递进…...
OpenBMC:编译
1.安装依赖 OpenBMC是基于Yocto搭建的,基于不同的OS预先需要安装的依赖包和工具,清参考: 1 System Requirements — The Yocto Project 5.1.999 documentation 2.下载代码 OpenBMC的源码位于: openbmc/openbmc: OpenBMC Distri…...
Effective Objective-C 2.0 读书笔记—— objc_msgSend
Effective Objective-C 2.0 读书笔记—— objc_msgSend 文章目录 Effective Objective-C 2.0 读书笔记—— objc_msgSend引入——静态绑定和动态绑定OC之中动态绑定的实现方法签名方法列表 其他方法objc_msgSend_stretobjc_msgSend_fpretobjc_msgSendSuper 尾调用优化总结参考文…...
