Netty HashedWheelTimer构建延迟队列

應用背景

  1. 定时心跳监测
  2. 对时间精度要求不高
  3. 订单成功后,在30分钟内没有支付,自动取消订单
  4. 外卖平台发送订餐通知,下单成功后60s给用户推送短信。
  5. 如果订单一直处于某一个未完结状态时,及时处理关单,并退还库存
  6. 淘宝新建商户一个月内还没上传商品信息,将冻结商铺等

原理简介

jdk自带延迟队列 DelayQueue缺陷

  • 毕竟是jdk级别的,不可能做过多的封装。很多API并不是那么好直接使用。比如直接传入一个延迟时间是并不能自动实现的,需要手动封装。

  • DelayQueue并没有长度限制。有内存占用的风险。

  • 效率,稳定性方面,在DelayQueue本身肯定是没有问题的,但是在项目中使用,势必需要做一些封装,直接上生产环境心里并没有底。

HashedWheelTimer

       netty毕竟是一个大名鼎鼎的框架,广泛使用于业界。它有许多心跳检测等定时任务,使用延时队列来实现。HashedWheelTimer底层数据结构依然是使用DelayedQueue。加上一种叫做时间轮的算法来实现。

       关于时间轮算法,有点类似于HashMap。在new 一个HashedWheelTimer实例的时候,可以传入几个参数。

       第一,一个时间长度,这个时间长度跟具体任务何时执行没有关系,但是跟执行精度有关。这个时间可以看作手表的指针循环一圈的长度。

       然后第二,刻度数。这个可以看作手表的刻度。比如第一个参数为24小时,刻度数为12,那么每一个刻度表示2小时。时间精度只能到两小时。时间长度/刻度数值越大,精度越大。

       添加一个任务的时候,根据hash算法得到hash值并对刻度数求模得到一个下标,这个下标就是刻度的位置。

       有一些任务的执行周期超过了第一个参数,比如超过了24小时,就会得到一个圈数round。

       简点说,添加一个任务时会根据任务得到一个hash值,并根据时间轮长度和刻度得到一个商值round和模index,比如时间长度24小时,刻度为12,延迟时间为32小时,那么round=1,index=8。时间轮从开启之时起每24/12个时间走一个指针,即index+1,第一圈round=0。当走到第7个指针时,此时index=7,此时刚才的任务并不能执行,因为刚才的任务round=1,必须要等到下一轮index=7的时候才能执行。

简单的解释图片来自网络
灵魂画手

多说无益,撸代码


import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 2019年5月10日16:41:48
 * changjiabo@juneyaoair.com
 * 测试延迟加载
 */
public class HashedWheelTimerTest {

    public static void main(String[] args) {

        final Timer timer = new HashedWheelTimer(Executors.defaultThreadFactory(), 5, TimeUnit.SECONDS, 2);
        TimerTask task1 = new TimerTask() {
            public void run(Timeout timeout) throws Exception {
                System.out.println("task 1 will run per 5 seconds ");
                timer.newTimeout(this, 5, TimeUnit.SECONDS);//结束时候再次注册
            }
        };
        timer.newTimeout(task1, 5, TimeUnit.SECONDS);
        TimerTask task2 = new TimerTask() {
            public void run(Timeout timeout) throws Exception {
                System.out.println("task 2 will run per 10 seconds");
                timer.newTimeout(this, 10, TimeUnit.SECONDS);//结束时候再注册
            }
        };

        timer.newTimeout(task2, 10, TimeUnit.SECONDS);

        //该任务仅仅运行一次
        timer.newTimeout(new TimerTask() {
            public void run(Timeout timeout) throws Exception {
                System.out.println("task 3 run only once ! ");
            }
        }, 15, TimeUnit.SECONDS);

    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值