你真的会用延时队列吗(一)

1 使用场景

  • 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。

  • 清理过期数据业务上。比如缓存中的对象,超过了空闲时间,需要从缓存中移出。

  • 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。

  • 下单之后如果三十分钟之内没有付款就自动取消订单。

  • 订餐通知:下单成功后60s之后给用户发送短信通知。

  • 当订单一直处于未支付状态时,如何及时的关闭订单,并退还库存?

  • 如何定期检查处于退款状态的订单是否已经退款成功?

  • 新创建店铺,N天内没有上传商品,系统如何知道该信息,并发送激活短信?

  • 定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行。

2 延时任务-实现方式

  • 定期轮询(数据库等)

  • DelayQueue

  • Timer

  • ScheduledExecutorService

  • 时间轮(kafka)

  • RabbitMQ

  • Quartz

  • Redis Zset

  • Koala

  • JCronTab

  • SchedulerX(阿里)

  • 有赞延迟队列

2.1 轮询

特点:定期轮训数据库,设置状态。

优点:实现简单	
缺点:数据量过大时会消耗太多的IO资源,效率太低
2.2 DelayQueue

特点: 无界、延迟、阻塞队列

a、BlockingQueue+PriorityQueue(堆排序)+Delayed	
b、DelayQueue中存放的对象需要实现compareTo()方法和getDelay()方法。	
c、getDelay方法返回该元素距离失效还剩余的时间,当<=0时元素就失效了,	
就可以从队列中获取到。

640?wx_fmt=png

640?wx_fmt=png

这里为什么要用leader/follower模式?

  • 如果不是队首节点,根本不需要唤醒操作!

  • 假设取值时,延时时间还没有到,那么需要等待,但这个时候,队列中新加入了一个延时更短的,并放在了队首,那么 此时,for循环由开始了,取得是新加入的元素,那之前的等待就白等了,明显可以早点退出等待!

  • 还有就是如果好多线程都在此等待,如果时间到了,同时好多线程会充等待队列进入锁池中,去竞争锁资源,但结果只能是一个成功, 多了写无畏的竞争!(多次的等待和唤醒)640?wx_fmt=png

2.3 Timer与TimerTask

640?wx_fmt=png

  • TaskQueue中的排序是对TimerTask中的下一次执行时间进行堆排序,每次去取数组第一个。

  • 而delayQueue是对queue中的元素的getDelay()结果进行排序

Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它可以计划执行一个任务一次或反复多次。 主要方法:640?wx_fmt=png

640?wx_fmt=png

2.4 时间轮(kafka)

时间轮名词解释:

  • 时间格:环形结构中用于存放延迟任务的区块;

  • 指针(CurrentTime):指向当前操作的时间格,代表当前时间

  • 格数(ticksPerWheel):为时间轮中时间格的个数

  • 间隔(tickDuration):每个时间格之间的间隔

  • 总间隔(interval):当前时间轮总间隔,也就是等于ticksPerWheel*tickDuration

640?wx_fmt=png

640?wx_fmt=png

根据每个TimerTaskEntry的过期时间和当前时间轮的时间,选择一个合适的bucket(实际上就是TimerTaskList),把这个TimerTaskEntry对象放进去,同时如果bucket的过期时间有更新,就将这个bucket推进DelayQueue,重新排序

例子:假设编号为0的时间格或者桶保存着到期时间为t,每一个tick的持续时间(tickDuration)为20ms,在这个格子里只能保存着到期时间为[t~t+20]ms的任务,假设时间轮的时间格有n个,每一个间隔1ms,到期时间为m(ms),那么计算公式m%n = 所在的时间格或者桶,比如n=10,m=34ms,那么他所在桶或者时间格是4

2.5 RabbitMQ-延时任务

RabbitMQ本身没有直接支持延迟队列功能,但是可以通过以下特性模拟出延迟队列的功能。

RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letter RabbitMQ针对队列中的消息过期时间有两种方法可以设置。 A: 通过队列属性设置,队列中所有消息都有相同的过期时间。 B: 对消息进行单独设置,每条消息TTL可以不同。

640?wx_fmt=png

2.6 Quartz

640?wx_fmt=png

为什么不用Timer?

  • Timers没有持久化机制.

  • Timers不灵活 (只可以设置开始时间和重复间隔,不是基于时间、日期、天等(秒、分、时)的)

  • Timers 不能利用线程池,一个timer一个线程

  • Timers没有真正的管理计划

核心概念:调度器、任务和触发器。

三者关系:调度器负责调度各个任务,到了某个时刻或者过了一定时间,触发器触动了,特定任务便启动执行。

640?wx_fmt=png

  1. scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。

  2. JobDetail是一个可执行的工作,它本身是有状态的。

  3. Trigger代表什么时候去调。

  4. 当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。

  5. scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。

待续。。。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值