承接上文

承接之前的【精华推荐 |【算法数据结构专题】「延时队列算法」史上非常详细分析和介绍如何通过时间轮(TimingWheel)实现延时队列的原理指南】,让我们基本上已经知道了「时间轮算法」原理和核心算法机制,接下来我们需要面向于实战开发以及落地角度进行分析如何实现时间轮的算法机制体系。


前言回顾

什么是时间轮

  • 调度模型:时间轮是为解决高效调度任务而产生的调度模型/算法思想。
  • 数据结构:通常由hash表和双向链表实现的数据结构。

为什么用时间轮?

对比传统队列的优势

相比传统的队列形式的调度器来说,时间轮能够批量高效的管理各种延时任务、周期任务、通知任务等等。例如延时队列/延时任务体系

延时任务/队列体系

**延时任务、周期性任务,应用场景主要在延迟大规模的延时任务、周期性的定时任务等。

案例-Kafka的延时操作系列

比如,对于耗时的网络请求(比如Produce时等待ISR副本复制成功)会被封装成DelayOperation进行延迟处理操作,防止阻塞Kafka请求处理线程,从而影响效率和性能。

传统队列带来的性能问题

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

基于JDK自带的Timer+DelayQueue实现

JDK Timer和DelayQueue底层都是个优先队列,即采用了minHeap的数据结构,最快需要执行的任务排在队列第一个,不一样的是Timer中有个线程去拉取任务执行,DelayQueue其实就是个容器,需要配合其他线程工作。

ScheduledThreadPoolExecutor是JDK的定时任务实现的一种方式,其实也就是DelayQueue+池化线程的一个实现

基于时间轮TimeWheel的实现

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

当流量小时,使用DelayQueue效率更高。当流量大事,使用时间轮将大大提高效率。

时间轮算法是怎么样的,算法思想是什么?

时间轮的数据结构

数据结构模型主要由:时间轮环形队列和时间轮任务列表组成。

  • 时间轮(TimingWheel) 是一个存储定时任务的环形队列(cycle array),底层采用环形数组实现,数组中的每个元素可以对应一个时间轮任务任务列表(TimeWheelTaskList)

  • 时间轮任务任务列表(TimeWheelTaskList) 是一个环形的双向链表,其中的每个元素都是延时/定时任务项(TaskEntry),其中封装了任务基本信息和元数据(Metadata)。

可以看到图中的几个参数:

  • startMs: 开始时间

  • tickMs: 时间轮执行的最小单位时间轮由多个时间格组成,每个时间格代表当前时间轮的基本时间跨度就是tickMs。

  • wheelSize: 时间轮中环形队列的数量时间轮的时间格数量是固定的,可用wheelSize来表示。

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

根据上面这两个属性,我们就可以讲整个时间轮的总体时间跨度(interval)可以通过公式tickMs × wheelSize计算得出。例如果时间轮的tickMs=1mswheelSize=20,那么可以计算得出interval为20ms

currentTime游标指针

此外,时间轮还有一个游标指针,我们称之为(currentTime),它用来表示时间轮当前所处的时间,currentTime是tickMs的整数倍。

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

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

currentTime游标指针的运作流程
  1. 初始情况下表盘指针currentTime指向时间格0,此时有一个定时为2ms的任务插入进来会存放到时间格为2的任务列表中。

  2. 随着时间的不断推移,指针currentTime不断向前推进,过了2ms之后,当到达时间格2时,就需要将时间格2所对应的任务列表中的任务做相应的到期操作。

  3. 此时若又有一个定时为8ms的任务插入进来,则会存放到时间格10中,currentTime再过8ms后会指向时间格10。

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

层次化时间轮机制

如果当提交了超过整体跨度(interval)的延时任务,如何解决呢?因此引入了层级时间轮的概念,当任务的到期时间超过了当前时间轮所表示的时间范围时,就会尝试添加到层次时间轮中。

层次化时间轮实现原理介绍

层次化时间轮任务升级跃迁

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

  • 第一层的时间轮tickMs=1ms, wheelSize=20, interval=20ms

  • 第二层的时间轮的tickMs为第一层时间轮的interval,即为20ms

第一层和第二层时间轮的wheelSize是固定的,都是20,那么第二层的时间轮的总体时间跨度interval为400ms。正好是第一层时间轮的20倍。以此类推,这个400ms也是第三层的tickMs的大小,第三层的时间轮的总体时间跨度为8000ms

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

层次化时间轮任务降级跃迁

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

案例介绍

层次化时间轮任务升级跃迁

例如:350ms的定时任务,显然第一层时间轮不能满足条件,所以就升级到第二层时间轮中,最终被插入到第二层时间轮中时间格17所对应的TimeWheelTaskList中。如果此时又有一个定时为450ms的任务,那么显然第二层时间轮也无法满足条件,所以又升级到第三层时间轮中,最终被插入到第三层时间轮中时间格1的TimeWheelTaskList中。

层次化时间轮任务降级跃迁

在 [400ms,800ms) 区间的多个任务(比如446ms、455ms以及473ms的定时任务)都会被放入到第三层时间轮的时间格1中,时间格1对应的TimerTaskList的超时时间为400ms

随着时间的流逝,当次TimeWheelTaskList到期之时,原本定时为450ms的任务还剩下50ms的时间,还不能执行这个任务的到期操作。

这里就有一个时间轮降级的操作,会将这个剩余时间为50ms的定时任务重新提交到层级时间轮中,此时第一层时间轮的总体时间跨度不够,而第二层足够,所以该任务被放到第二层时间轮到期时间为[40ms,60ms)的时间格中。

再经历了40ms之后,此时这个任务又被“察觉”到,不过还剩余10ms,还是不能立即执行到期操作。所以还要再有一次时间轮的降级,此任务被添加到第一层时间轮到期时间为[10ms,11ms)的时间格中,之后再经历10ms后,此任务真正到期,最终执行相应的到期操作。

后续章节预告

接下来小编会出【算法数据结构专题】「延时队列算法」史上手把手教你针对层级时间轮(TimingWheel)实现延时队列的开发实战落地(下),进行实战编码进行开发对应的层次化的时间轮算法落地。

【算法数据结构专题】「延时队列算法」史上手把手教你针对层级时间轮(TimingWheel)实现延时队列的开发实战落地(上)的更多相关文章

  1. 搜索引擎算法研究专题六:HITS算法

    搜索引擎算法研究专题六:HITS算法 2017年12月19日 ⁄ 搜索技术 ⁄ 共 1240字 ⁄ 字号 小 中 大 ⁄ 评论关闭   HITS(Hyperlink-Induced Topic Sea ...

  2. 搜索引擎算法研究专题七:Hilltop算法

    搜索引擎算法研究专题七:Hilltop算法 2017年12月19日 ⁄ 搜索技术 ⁄ 共 1256字 ⁄ 字号 小 中 大 ⁄ 评论关闭   HillTop也是搜索引擎结果排序的专利,是Google工 ...

  3. ☕【Java深层系列】「技术盲区」让我们一起完全吃透针对于时间和日期相关的API指南

    技术简介 java中的日期处理一直是个问题,没有很好的方式去处理,所以才有第三方框架的位置比如joda.文章主要对java日期处理的详解,用1.8可以不用joda. 时间概念 首先我们对一些基本的概念 ...

  4. 【Dubbo3终极特性】「云原生三中心架构」带你探索Dubbo3体系下的配置中心和元数据中心、注册中心的原理及开发实战(上)

    Dubb3的应用级服务发现 Dubbo3提供了全新的应用级服务发现模型,该模型在设计与实现上区别于 Dubbo2 的接口级服务发现模型. 概括来说,Dubbo3 引入的应用级服务发现主要有以下优势 适 ...

  5. 算法数据结构 | 只要30行代码,实现快速匹配字符串的KMP算法

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法数据结构专题的第29篇文章,我们来聊一个新的字符串匹配算法--KMP. KMP这个名字不是视频播放器,更不是看毛片,它其实是由Kn ...

  6. 算法数据结构 | 三个步骤完成强连通分量分解的Kosaraju算法

    强连通分量分解的Kosaraju算法 今天是算法数据结构专题的第35篇文章,我们来聊聊图论当中的强连通分量分解的Tarjan算法. Kosaraju算法一看这个名字很奇怪就可以猜到它也是一个根据人名起 ...

  7. 延时任务-基于netty时间轮算法实现

    一.时间轮算法简介 为了大家能够理解下文中的代码,我们先来简单了解一下netty时间轮算法的核心原理 时间轮算法名副其实,时间轮就是一个环形的数据结构,类似于表盘,将时间轮分成多个bucket(比如: ...

  8. 「编程羽录」上线,程序员必备的这些技能你能get到嘛?

    大家好,我是小羽. 好久不见,给大家带来个好消息,小羽的全新专题「编程羽录」系列正式上新,主要是介绍一些关于面试题和经验总结的文章. 会为大家提供一些技术栈之外,程序员还需要的其他方面硬核知识,做到全 ...

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

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

  10. LibreOJ #6008. 「网络流 24 题」餐巾计划 最小费用最大流 建图

    #6008. 「网络流 24 题」餐巾计划 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

随机推荐

  1. #科技 #资讯 #生活 微信测试更多图片打开方式,神州圆满发射,英伟达或停产性价比神卡,SAMSUNG新一代显存带宽容量双翻倍,这就是今天的其它大新闻

    今天是2022年12月01日 十一月初八 现在是中午12:10 下面是今天的其他大新闻 #NEWS 1 # 微信测试用不同小程序打开图片:快捷调用小程序打开图片.视频.文件 ( 新浪科技 ) 据悉,微 ...

  2. c++ read and save txt

    read and save #include "util/image_util.h" #ifdef USE_PANGOLIN_VIEWER #include "pango ...

  3. VMWare安装CentOS 7系统 & 操作系统优化

    1.准备工作 (1)VMWare 14:https://download3.vmware.com/software/wkst/file/VMware-workstation-full-14.1.1-7 ...

  4. ybtoj 12F

    求值的话改为求解前缀和的值,通过两个前缀和相减即可得到每个值. 每次询问相当于给一个方程. 一共有 $n$ 个未知数,因此需要 $n$ 个方程,同时每个数都必须至少在方程中出现一次. 最小生成树求解即 ...

  5. 树莓派3B+ wifi设置

    环境: 硬件:树莓派 3b+ 固件:2018-04-18-raspbian-stretch.img 一.树莓派配置 1.1.wifi配置方法一(已测试,ok) 参考: https://www.cnbl ...

  6. Mysql5.7的安装与卸载与数据迁移

    Mysql5.7的安装与卸载 安装: 1.安装的时候 只选择安装 server即可 2.安装过程中,一定要重新选择安装路径和各个日志的路径,将来会非常大,不适合存在系统盘下面 3.安装时,取消MySQ ...

  7. C/C++ 数据结构循环队列的实现

    #include <iostream> #include <Windows.h> using namespace std; #define MAXSIZE 6 typedef ...

  8. String的Equals()和==比较

    String的Equals()和==比较 常量池(Constant Pool):指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据.JVM虚拟机为每个被装载的类型维护一个常量池.常量 ...

  9. Java反射机制知识

    modifier:修饰语 名词 JAVA 反射机制中,Field的getModifiers()方法返回int类型值表示该字段的修饰符. 其中,该修饰符是java.lang.reflect.Modifi ...

  10. 文件搜索失败:cannot update repo 'Media": No LRO_ URLS, LRO_MIRRORLISTURL nOr LRO _METALINKURL specified

    配置yum源 1.产看当前系统版本 cat /etc/redhat-release 2.进入/etc/yum.repos.d目录,新建bak目录,将系统自带的yum移到bak目录 cd /etc/yu ...