范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

SpringBoot任务经典定时任务设计时间轮(TimeWheel)案例和原理

  Timer和ScheduledExecutorService是JDK内置的定时任务方案,而业内还有一个经典的定时任务的设计叫时间轮(Timing Wheel), Netty内部基于时间轮实现了一个HashedWheelTimer来优化百万量级I/O超时的检测,它是一个高性能,低消耗的数据结构,它适合用非准实时,延迟的短平快任务,例如心跳检测。本文主要介绍时间轮(Timing Wheel)及其使用。@pdai知识准备
  需要对时间轮(Timing Wheel),以及Netty的HashedWheelTimer要解决什么问题有初步的认识。   什么是时间轮(Timing Wheel)
  时间轮(Timing Wheel)是George Varghese和Tony Lauck在1996年的论文"Hashed and Hierarchical Timing Wheels: data structures to efficiently implement a timer facility"实现的,它在Linux内核中使用广泛,是Linux内核定时器的实现方法和基础之一。
  时间轮(Timing Wheel)是一种环形的数据结构,就像一个时钟可以分成很多格子(Tick),每个格子代表时间的间隔,它指向存储的具体任务(timerTask)的一个链表。
  以上述在论文中的图片例子,这里一个轮子包含8个格子(Tick), 每个tick是一秒钟;
  任务的添加 :如果一个任务要在17秒后执行,那么它需要转2轮,最终加到Tick=1位置的链表中。
  任务的执行 :在时钟转2Round到Tick=1的位置,开始执行这个位置指向的链表中的这个任务。(# 这里表示剩余需要转几轮再执行这个任务) Netty的HashedWheelTimer要解决什么问题
  HashedWheelTimer是Netty根据时间轮(Timing Wheel)开发的工具类,它要解决什么问题呢?这里面有两个要点:  延迟任务 + 低时效性  。@pdai
  在Netty中的一个典型应用场景是判断某个连接是否idle,如果idle(如客户端由于网络原因导致到服务器的心跳无法送达),则服务器会主动断开连接,释放资源。判断连接是否idle是通过定时任务完成的,但是Netty可能维持数百万级别的长连接,对每个连接去定义一个定时任务是不可行的,所以如何提升I/O超时调度的效率呢?
  Netty根据时间轮(Timing Wheel)开发了HashedWheelTimer工具类,用来优化I/O超时调度(本质上是延迟任务);之所以采用时间轮(Timing Wheel)的结构还有一个很重要的原因是I/O超时这种类型的任务对时效性不需要非常精准。  HashedWheelTimer的使用方式
  在了解时间轮(Timing Wheel)和Netty的HashedWheelTimer要解决的问题后,我们看下HashedWheelTimer的使用方式
  通过构造函数看主要参数  public HashedWheelTimer(        ThreadFactory threadFactory,        long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,        long maxPendingTimeouts, Executor taskExecutor) {}
  具体参数说明如下:  threadFactory :线程工厂,用于创建工作线程, 默认是Executors.defaultThreadFactory() tickDuration :tick的周期,即多久tick一次 unit : tick周期的单位 ticksPerWheel :时间轮的长度,一圈下来有多少格 leakDetection :是否开启内存泄漏检测,默认是true maxPendingTimeouts :最多执行的任务数,默认是-1,即不限制。在高并发量情况下才会设置这个参数。 实现案例
  这里展示下HashedWheelTimer的基本使用案例。@pdai   Pom依赖
  引入pom的依赖      io.netty    netty-all    4.1.77.Final2个简单例子
  例子1:5秒后执行TimerTask  @SneakyThrowspublic static void simpleHashedWheelTimer() {    log.info("init task 1...");        HashedWheelTimer timer = new HashedWheelTimer(1, TimeUnit.SECONDS, 8);    // add a new timeout    timer.newTimeout(timeout -> {        log.info("running task 1...");    }, 5, TimeUnit.SECONDS);}
  执行结果如下:  23:32:21.364 [main] INFO tech.pdai.springboot.schedule.timer.netty.HashedWheelTimerTester - init task 1......23:32:27.454 [pool-1-thread-1] INFO tech.pdai.springboot.schedule.timer.netty.HashedWheelTimerTester - running task 1...
  例子2:任务失效后cancel并让它重新在3秒后执行。  @SneakyThrowspublic static void reScheduleHashedWheelTimer() {    log.info("init task 2...");    HashedWheelTimer timer = new HashedWheelTimer(1, TimeUnit.SECONDS, 8);    Thread.sleep(5000);    // add a new timeout    Timeout tm = timer.newTimeout(timeout -> {        log.info("running task 2...");    }, 5, TimeUnit.SECONDS);    // cancel    if (!tm.isExpired()) {        log.info("cancel task 2...");        tm.cancel();    }    // reschedule    timer.newTimeout(tm.task(), 3, TimeUnit.SECONDS);}23:28:36.408 [main] INFO tech.pdai.springboot.schedule.timer.netty.HashedWheelTimerTester - init task 2...23:28:41.412 [main] INFO tech.pdai.springboot.schedule.timer.netty.HashedWheelTimerTester - cancel task 2...23:28:45.414 [pool-2-thread-1] INFO tech.pdai.springboot.schedule.timer.netty.HashedWheelTimerTester - running task 2...进一步理解
  我们通过如下问题进一步理解HashedWheelTimer。@pdai   HashedWheelTimer是如何实现的?
  简单看下HashedWheelTimer是如何实现的
  Worker :worker工作线程主要负责任务调度触发,单线程运行。 HashedWheelBucket : 时间轮上面的格子,内部持有HashedWheelTimeout组成的链表结构的头尾节点,多个格子组成的时间轮形成一圈又一圈的任务环 HashedWheelTimeout : 往时间轮里面提交的任务会被封装成HashedWheelTimeout
  构造函数  public HashedWheelTimer(        ThreadFactory threadFactory,        long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,        long maxPendingTimeouts, Executor taskExecutor) {    checkNotNull(threadFactory, "threadFactory");    checkNotNull(unit, "unit");    checkPositive(tickDuration, "tickDuration");    checkPositive(ticksPerWheel, "ticksPerWheel");    this.taskExecutor = checkNotNull(taskExecutor, "taskExecutor");    // Normalize ticksPerWheel to power of two and initialize the wheel.    wheel = createWheel(ticksPerWheel);    mask = wheel.length - 1;    // Convert tickDuration to nanos.    long duration = unit.toNanos(tickDuration);    // Prevent overflow.    if (duration >= Long.MAX_VALUE / wheel.length) {        throw new IllegalArgumentException(String.format(                "tickDuration: %d (expected: 0 < tickDuration in nanos < %d",                tickDuration, Long.MAX_VALUE / wheel.length));    }    if (duration < MILLISECOND_NANOS) {        logger.warn("Configured tickDuration {} smaller than {}, using 1ms.",                    tickDuration, MILLISECOND_NANOS);        this.tickDuration = MILLISECOND_NANOS;    } else {        this.tickDuration = duration;    }    workerThread = threadFactory.newThread(worker);    leak = leakDetection || !workerThread.isDaemon() ? leakDetector.track(this) : null;    this.maxPendingTimeouts = maxPendingTimeouts;    if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&        WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {        reportTooManyInstances();    }}
  创建wheel  private static HashedWheelBucket[] createWheel(int ticksPerWheel) {    //ticksPerWheel may not be greater than 2^30    checkInRange(ticksPerWheel, 1, 1073741824, "ticksPerWheel");    ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);    HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];    for (int i = 0; i < wheel.length; i ++) {        wheel[i] = new HashedWheelBucket();    }    return wheel;}private static int normalizeTicksPerWheel(int ticksPerWheel) {    int normalizedTicksPerWheel = 1;    while (normalizedTicksPerWheel < ticksPerWheel) {        normalizedTicksPerWheel <<= 1;    }    return normalizedTicksPerWheel;}
  任务的添加  @Overridepublic Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {    checkNotNull(task, "task");    checkNotNull(unit, "unit");    long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();    if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {        pendingTimeouts.decrementAndGet();        throw new RejectedExecutionException("Number of pending timeouts ("            + pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "            + "timeouts (" + maxPendingTimeouts + ")");    }    start();    // Add the timeout to the timeout queue which will be processed on the next tick.    // During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.    long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;    // Guard against overflow.    if (delay > 0 && deadline < 0) {        deadline = Long.MAX_VALUE;    }    HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);    timeouts.add(timeout);    return timeout;}
  执行方法  /**    * Starts the background thread explicitly.  The background thread will    * start automatically on demand even if you did not call this method.    *    * @throws IllegalStateException if this timer has been    *                               {@linkplain #stop() stopped} already    */public void start() {    switch (WORKER_STATE_UPDATER.get(this)) {        case WORKER_STATE_INIT:            if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {                workerThread.start();            }            break;        case WORKER_STATE_STARTED:            break;        case WORKER_STATE_SHUTDOWN:            throw new IllegalStateException("cannot be started once stopped");        default:            throw new Error("Invalid WorkerState");    }    // Wait until the startTime is initialized by the worker.    while (startTime == 0) {        try {            startTimeInitialized.await();        } catch (InterruptedException ignore) {            // Ignore - it will be ready very soon.        }    }}
  停止方法  @Overridepublic Set stop() {    if (Thread.currentThread() == workerThread) {        throw new IllegalStateException(                HashedWheelTimer.class.getSimpleName() +                        ".stop() cannot be called from " +                        TimerTask.class.getSimpleName());    }    if (!WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_STARTED, WORKER_STATE_SHUTDOWN)) {        // workerState can be 0 or 2 at this moment - let it always be 2.        if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) {            INSTANCE_COUNTER.decrementAndGet();            if (leak != null) {                boolean closed = leak.close(this);                assert closed;            }        }        return Collections.emptySet();    }    try {        boolean interrupted = false;        while (workerThread.isAlive()) {            workerThread.interrupt();            try {                workerThread.join(100);            } catch (InterruptedException ignored) {                interrupted = true;            }        }        if (interrupted) {            Thread.currentThread().interrupt();        }    } finally {        INSTANCE_COUNTER.decrementAndGet();        if (leak != null) {            boolean closed = leak.close(this);            assert closed;        }    }    return worker.unprocessedTimeouts();}什么是多级Timing Wheel?
  多级的时间轮是比较好理解的,时钟是有小时,分钟,秒的,秒转一圈(Round)分钟就转一个格(Tick), 分钟转一圈(Round)小时就转一格(Tick)。
  PS:显然HashedWheelTimer是一层时间轮。  示例源码
  https://github.com/realpdai/tech-pdai-spring-demos  更多内容
  告别碎片化学习,无套路一站式体系化学习后端开发: Java 全栈知识体系 https://pdai.tech

比亚迪海豹上市,来盘点这辆豹款的黑科技车友们期待已久的新能源轿跑比亚迪海豹终于在5月20日公布了售价,开始上市预售,四款车型分别为550km标准续航后驱版精英型(21。28万元)550km标准续航后驱版尊贵型(22。5在巴拿马人眼里,运河区是一个高不可攀的地方1903年的美巴条约美国占领了那片土地,对运河地区拥有主权,有权修建和管理运河。运河区位于巴拿马共和国中部,毗邻巴拿马城,将全国一分为二。如果你想去巴拿马西部的乡下,你必须穿过运河张常宁夫妇520秀恩爱!郎才女貌,一起吃棒棒糖,下个月举办婚礼在中国体坛,张常宁和吴冠希是秀恩爱频率非常高的一对,520,中国情人节,张常宁和吴冠希先后发声秀恩爱。下个月,两人就要举办盛大的婚礼了,场面让人期待。张常宁写道,一起度过的第五个5记者姆巴佩若留队实际税后年薪8400万欧,远远超出薪资纪录直播吧5月20日讯记者TancrediPalmeri在社交媒体上表示,如果姆巴佩与巴黎续约,这位法国前锋平均每年将拿到8400万欧元的税后收入。根据此前媒体的报道,姆巴佩可能会改变泰坦尼克号幸存的6个中国人,为何被污蔑了一百多年?1997年,卡梅隆巨作泰坦尼克号正式上映,次年,这部电影在中国引发了巨大的轰动。一时间,杰克与露丝的爱情,成为中国家喻户晓的故事。时至今日,它都是人们心中无法撼动的经典之作,只是在张檬与老公金恩圣练瑜伽,腹部赘肉抢镜,结婚证摆油烟机上引热议5月19日晚,张檬在个人社交平台分享出与老公金恩圣一起练习瑜伽的照片,并配文称居家健身,享受美好生活。前两天(5月17日)晒出与金恩圣婚纱照,因凸起的小腹被质疑怀孕的张檬,现在又分百亿女赌王何婉琪喜欢上小2岁堂弟,临终要何鸿燊还钱她曾被誉为澳门第一美人,人送外号百亿女赌王,在纸醉金迷的澳门赌场圈里有着崇高的地位。她虽为一介女流,但她的人生却比许多男儿都过得精彩,禁忌之恋商战浮沉豪门争斗,她的一生将这12个字离婚11年后,再看姚晨和凌潇肃的现状,相忘于江湖才是最好的结局提到姚晨,首先想到的可能是2006年播放的武林外传中,那个大大咧咧的郭芙蓉,在剧中咋咋呼呼,时不时还要甩出一击排山倒海。而在之后,姚晨出席各种活动,都给人以落落大方得体的形象,作为八三版电视剧霍元甲十大高手排名第一位霍元甲霍元甲是当之无愧的第一位,即便是陈真的武功最后修炼到炉火纯青的程度也未能超过霍元甲的武功修为。凭借着过人的天资,自学成才自成一派迷踪拳。首战独臂老人程天啸。霍元武和赵振婚后两年再看郭碧婷嫁入豪门向家,是她最合适的一条路在我家小两口的综艺节目里,郭碧婷和向佐自曝肾已经到了七十岁的功能。此话一出,震惊了现场的嘉宾,维嘉都惊掉了下巴。这时向太出来解释,称向佐从小习武,经常受伤,需要靠止痛药止疼,这才导圆明园被毁前的照片,简直是人间仙境,无疑是世界又一大奇迹中国有着悠久的历史和精彩的故事,这其中也是流传了很多的艺术,其中建筑艺术就是一种,在古代保存至今的建筑有很多,这都是体现了我国的文化。像现在的故宫就是我国古代的宫殿建筑群,其实在当
天鹅股份转型崴脚5000万款项难收回,中报扣非净利润六年连亏18日,天鹅股份发布了公司的2022年半年度报告,报告显示虽然公司营业收入实现增长,但是净利润却较去年下滑了25。08,而如果扣除非经常性损益,天鹅股份已经陷入亏损,从往年中报数据豫园股份2022年上半年营收220亿元,聚焦新赛道8月22日,豫园股份(600655。SH)发布2022年上半年业绩公告,报告期内,公司实现营业收入220。09亿元,归母净利润7。54亿元。虽然在二季度受到新冠疫情的冲击,但豫园股中国学者联合太阳能之父,提出通过控制应力实现长期稳定的新途径他叫兰东辰,在澳洲留学时师从光伏之父澳大利亚新南威尔士大学的马丁格林(MartinGreen)院士。如今他在浙大电气工程学院担任百人计划研究员。最近,其担任一作兼通讯的论文(由兰东A股抄底失败的原因是什么?你还想抄底吗?写给2亿中国股民市场的残酷之处在于,你需要为自己每一个不当决定买单,真金白银地买单。基于主观判断的程序化交易,本身就是一个矛盾体,所以学费是少不了的。徐宁总结经验,大概就是亏出一个大心脏。买完跌停黄光裕罕见低头回归一年半,国美没了827亿,直言活下去人们或许想不到,那个骄傲强势的国美教父,能说出时移势易这种颇为掉份的话。随着致国美朋友们的一封信的发布,不仅意味着黄光裕未能如约完成18个月让国美恢复市场地位的计划,更标志着这位昔今年第6次油价下调,油价要5连跌,95号汽油重回8元时代国内第16轮成品油调整将于(8月23日24时)开启,油价将要5连跌了。今天预期跌幅比上个工作日增加了4元吨,这让目前的统计数据依然超过50元吨的油价下调标准215元吨,就这个跌幅而女人过了60岁,真的不年轻了吗?两位女人说出心里话前言60岁实际上人生已经过了一大半这个时候,虽然没有进入老年状态,也说不上年轻。在我们的生活当中,有很多事情,我们一定要认真考虑懂得正确的生活方式非常关键。不要觉得生活很复杂,更不最好的生活状态有耐心有事做有期待你是不是总想快速做完一件事,还没开始做一半,你就已经迫不及待地等待结果了?工作之后的闲暇之余,你会无事可做,抱着手机刷几个小时,但还是会觉得内心空虚。你很难活在当下,不是焦虑过去,余秋雨经典语录30句夏日生活打卡季特别喜欢余秋雨的著作,品读以后整理了余秋雨经典语录30条欢迎大家阅读。1我们始终都在笑,最后变成了不敢哭的人。2最疼的疼是原谅,最黑的黑的绝望。3为什么把择定终身的职当你被人看不起时,牢记这些话,就是最好的回击文木盈公子你是否也曾经历过这样的日子?它焦虑迷茫,又遥遥无期,内心特别脆弱,渴望被拯救,但又无法从中彻底挣脱出来,在那段时间里,你看到了许多平常没有发生过的事。比如之前围绕在身边的谁是你生命中最重要的人我们每一个人的人生,其实最为靠得住的,唯有自己。你选择坚强,生命便会散发光芒,你选择怯懦,生命便会暗淡惆怅。我们依靠自己,但是有些时候真的是力所不及的时候,能依旧陪伴在身边的,其实晚上睡不着,白天坐凳子上直接就能睡着是不是失眠?很多上班的人基本上都会出现的一个问题就是每天工作压力大,晚上下班回到家有时候还要在家里面加班,从而导致晚上总是熬夜加班。这种现象时间久了就会发现自己晚上会习惯性地睡不着。晚上睡不着洋葱,生吃好还是熟吃好?不知道你就亏大了洋葱是被国际公认的健康食材,有着蔬菜皇后的美名。它富含钙膳食纤维蛋白质等营养素。除了营养好,养生保健作用也不少,尤其是在养护血管方面,那你知道洋葱生吃好还是熟吃好呢?先来看看洋葱有今日养生方立秋后,养肺多吃这十样食物立秋之后,天气虽然炎热,但气候不同于夏天。秋季时,身体能马上感知凉意,汗液津液往回走,但收敛得太过了,会让体表感觉干燥,比如鼻孔干燥嗓子干燥皮肤干燥毛发干枯小便赤黄大便干结等等。燥都说猪油是心血管杀手,为啥老一辈人常吃却没事?背后原因,看完瞬间醒悟说到猪油,有的人就馋得舔嘴,真的是太香了。一勺猪油配上一碗饭,是老一辈记忆里的年味。可如今,猪油被贴上不健康的标签,大家都对它避而远之。甚至有人说,猪油是心血管杀手,除了香一无是处男生想要变白,该如何去做才能有效变白?肤色由先天决定,与遗传和神经有关。有的人天生由于黑色素含量多,肤色就偏黑有的天生黑色素含量少,肤色就偏白。首先做到补水防晒,干燥的肌肤受到紫外线的侵袭更容易出现皮肤的暗沉,容易有色(外代二线)墨西哥时装周掠影(外代二线)墨西哥时装周掠影8月19日,模特在墨西哥瓦哈卡州米特拉举行的墨西哥时装周上展示设计师克里斯戈伊里设计的新款服装。新华社欧新8月19日,模特在墨西哥瓦哈卡州米特拉举行的墨54岁的周涛让我知道,原来中年女人穿连衣裙可以这么优雅现在的女明星的照片荧幕形象,看起来确实都非常养眼。因为年轻的身体,因为胶原蛋白的支撑,因为水分的充足,青春洋溢,很难让人不喜欢。当然也有不少女明星在年纪步入中年,还营销少女感。明星研究表明,每天吃两杯葡萄可以更长寿越来越多的证据支持食用天然食品(包括水果蔬菜和其他未加工食品)的积极影响。发表在食品杂志上的一系列新研究表明,葡萄的摄入可能对健康和死亡率有重大影响,特别是当添加到高脂肪的西方饮食政府扶持的中医药养生庄园,可以这样设计导读民族的才是世界的。如果说有什么亮点能够让休闲农业变得国际化,甚至赢得国际市场,那中医药肯定是非常重要的资源。以中医药为主题的养生农业是发展休闲农业的一个重要方向,不仅有着广阔的维多利亚为了保持身材,在英格兰球星的帮助下进行每周5次的锻炼综合英国媒体镜报8月19日讯,在前英格兰队球星的帮助下维多利亚贝克汉姆(VictoriaBeckham)成功改变了自己的身材。作为一名时装设计师,她一直保持着苗条的身材,她的丈夫大葡萄牙国家队将目标放在此次世界杯的冠军,夺冠几率十分大世界杯,葡萄牙队,冠军,c罗,强悍万众瞩目的卡塔尔世界杯比赛已经接近开始了,仅有不到三个月的时间就要正式开赛,相信很多球迷都将目光放眼到了葡萄牙这支强悍的球队,而作为葡萄牙的主力,