一、时间轮算法简介

为了大家能够理解下文中的代码,我们先来简单了解一下netty时间轮算法的核心原理



时间轮算法名副其实,时间轮就是一个环形的数据结构,类似于表盘,将时间轮分成多个bucket(比如:0-8)。假设每个时间轮轮片的分隔时间段tickDuration=1s(即:指针经过每个格子花费时间是 1 s),当前的时间bucket=3,那么在18秒后需要被执行的任务需要落到((3+18)%8=5取余运算)的5号bucket上。假如有多个需要在该时间段内执行的任务,就会组成一个双向链表。

另外针对时间轮我们要有下面的几个认知:

  • 时间轮指针是一个Worker线程,在时间轮整点的时候执行双向链表中的任务。
  • 时间轮算法的并不是精准的延时,它的执行精度取决于每个时间轮轮片的分隔时间段tickDuration
  • Worker线程是单线程,一个bucket、一个bucket的顺序处理任务。所以我们的延时任务一定要做成异步任务,否则会影响时间轮后续任务的执行时间。

二、时间轮hello-world

实现一个延时任务的例子,需求仍然十分的简单:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消。订单30分钟不付款自动取消,这个任务就是一个延时任务。 我们的火车票订单取消任务,从需求上看并不需要非常精准的延时,所以是可以使用时间轮算法来完成这个任务的。

首先通过maven坐标引入netty

<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.45.Final</version>
</dependency>

然后我们创建一个时间轮,如果是Spring的开发环境,我们可以这么做。下文中我们new了一个包含512个bucket的时间轮,每个时间轮的轮片时间间隔是100毫秒。

@Bean("hashedWheelTimer")
public HashedWheelTimer hashedWheelTimer(){
return new HashedWheelTimer(100, TimeUnit.MILLISECONDS, 512);
}

举例:当用户买火车票下单的时候,向时间轮中添加一个30分钟的延时任务。延时任务将在30分钟之后被执行,下文的lambda表达式部分实现了一个TimerTask(task)延时任务。这个延时任务的函数体内,请一定使用异步任务,即:单独起一个线程或者使用SpringBoot异步任务线程池。因为Worker线程是单线程的,你的任务处理时间长于tickDuration会妨碍后续时间轮轮片上的任务的执行。

//订单下单操作
void order(String orderInfo) {
//下单的时候,向时间轮中添加一个30分钟的延时任务
hashedWheelTimer.newTimeout(task -> {
//注意这里使用异步任务线程池或者开启线程进行订单取消任务的处理
cancelOrder(orderInfo);
}, 30, TimeUnit.MINUTES);
}

三、异步任务线程池

我们在上文中已经多次强调,时间轮的任务TimerTask的执行内容要做成异步的。最简单的做法就是接到一个任务之后启动一个线程处理该任务。在Spring环境下其实我们有更好的选择,就是使用Spring的线程池,这个线程池是可以自定义的。比如:下文中的用法是我事先定义了一个名字为test的线程池,然后通过@Async使用即可。

@Async("test")
public void cancelOrder(String orderInfo){
//查询订单支付信息,如果用户未支付,关闭订单
}

可能有的朋友,还不知道该如何自定义一个Spring线程池,可以参考:我之前写过一个SpringBoot的可观测、易配置的线程池开源项目,源代码地址:https://gitee.com/hanxt/zimug-monitor-threadpool 。我的这个zimug-monitor-threadpool开源项目,可以做到对线程池使用情况的监控,我自己平时用的效果还不错,向大家推荐一下!

四、时间轮优缺点

时间轮算法实现延时任务的优点就是,相对于使用JDK的DelayQueue,其算法上具有优势,执行性能相对好一些。

其缺点就是所有的延时任务以及延时触发的管理,都是在单个应用服务的内存中进行的,一旦该应用服务发生故障重启服务,时间轮任务数据将全部丢失。这一缺点和DelayQueue是一样的。为了解决这个问题,我们可以使用redis、RocketMQ等分布式中间件来管理延时任务消息的方式来实现延时任务,这个我会在后续的文章中为大家介绍。

欢迎关注我的公告号:字母哥杂谈,回复003赠送作者专栏《docker修炼之道》的PDF版本,30余篇精品docker文章。字母哥博客:zimug.com

延时任务-基于netty时间轮算法实现的更多相关文章

  1. 时间轮算法在Netty和Kafka中的应用,为什么不用Timer、延时线程池?

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

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

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

  3. .Net 之时间轮算法(终极版)

    关于时间轮算法的起始 我也认真的看了时间轮算法相关,大致都是如下的一个图 个人认为的问题 大部分文章在解释这个为何用时间轮的时候都再说 假设我们现在有一个很大的数组,专门用于存放延时任务.它的精度达到 ...

  4. .Net之时间轮算法(终极版)定时任务

    TimeWheelDemo 一个基于时间轮原理的定时器 对时间轮的理解 其实我是有一篇文章(.Net 之时间轮算法(终极版))针对时间轮的理论理解的,但是,我想,为啥我看完时间轮原理后,会采用这样的方 ...

  5. 时间轮算法的定时器(Delphi)

    源码下载 http://files.cnblogs.com/lwm8246/uTimeWheel.rar D7,XE2 编译测试OK //时间轮算法的定时器 //-- : QQ unit uTimeW ...

  6. 延时任务-基于redis zset的完整实现

    所谓的延时任务给大家举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消.订单30分钟不付款自动取消,这个任务就是一个延时任务. 我之前已经写过2篇关于延时任务的文章: <完 ...

  7. Rust 实现Netty HashedWheelTimer时间轮

    目录 一.背景 二.延迟队列-时间轮 三.Netty 时间轮源码分析 四.Rust实现HashedWheelTimer 五.总结思考 一.背景 近期在内网上看到一篇文章,文中提到的场景是 系统自动取消 ...

  8. [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用

    [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 目录 [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 0x00 摘要 0x01 业务领域 1.1 应用场景 0x02 定 ...

  9. kafka时间轮的原理(一)

    概述 早就想写关于kafka时间轮的随笔了,奈何时间不够,技术感觉理解不到位,现在把我之前学习到的进行整理一下,以便于以后并不会忘却.kafka时间轮是一个时间延时调度的工具,学习它可以掌握更加灵活先 ...

随机推荐

  1. Mysql 存储引擎以及 SQL语句

    存储引擎 文件格式有很多种,针对不同的文件格式会有对应的不同存储方式和处理机制. 针对不同的数据应该有对应的不同处理机制来存储. 存储引擎就是不同的处理机制 MySQL主要的存储引擎 Innodb 是 ...

  2. Java命令行传递参数

    目录 命令行传参 代码运行 视频 命令行传参 有时候你希望运行一个程序的时候再传递给它消息. 这要靠传递命令行参数给main()函数实现 package com.broky.base; public ...

  3. mysql 开启binlog日志,恢复误删的表、数据、mysql库

    linux下开启mysql的binlog日志功能 1.配置mysql配置文件my.cnf(内容如下). #配置文件储存的位置log-bin=mysql-bin#5.7以及以上版本需要配置这一行(保证唯 ...

  4. Python爬虫-正则

    介绍: 是 一门全新的语言,一种使用表达式的方式对字符串进行匹配的语法规则 我们抓取到的网页源代码本质上就是一个超长的字符串,想从里面提取内容,用正则再适合不过 优点:速度快.效率高.准确性高 缺点: ...

  5. SAP Smart Form 无法通过程序自定义默认打印机问题解决

    *&---------------------------------------------------------------------* *& Form FRM_SET_PRI ...

  6. Array 数组filter()方法,遍历数组返回符合条件的一个新数组

    <script> const arr = [ { id: 1, name: "aa", isDone: false }, { id: 2, name: "bb ...

  7. Metasploit(msf)利用ms17_010(永恒之蓝)出现Encoding::UndefinedConversionError问题

    Metasploit利用ms17_010(永恒之蓝) 利用流程 先确保目标靶机和kali处于同一网段,可以互相Ping通 目标靶机防火墙关闭,开启了445端口 输入search ms17_010 搜索 ...

  8. Unity3D学习笔记6——GPU实例化(1)

    目录 1. 概述 2. 详论 3. 参考 1. 概述 在之前的文章中说到,一种材质对应一次绘制调用的指令.即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成两次 ...

  9. Map接口中的常用方法和Map集合遍历键找值方式

    Map接口中定义了很多方法,常用的如下: public V put(K key,V value) 将指定的值与此映射中的指定键相关联(可选操作) V remove(Object key); 如果此映射 ...

  10. dubbo(九):timeout超时机制解析

    在网络请求时,总会有各种异常情况出现,我们需要提前处理这种情况.在完善的rpc组件dubbo中,自然是不会少了这一层东西的.我们只需要通过一些简单的配置就可以达到超时限制的作用了. dubbo的设计理 ...