延时队列
深夜,凝芳殿中。 "奴婢参见玉妃,玉妃娘娘万福金安",林婉拜跪着说道。 身前不远处,坐着一位天生媚骨,身材极好的女子,此刻她正慵懒的抱着一只波斯猫,轻轻的抚摸着。 "本宫听闻浣衣监新来了个婢子,是工部尚书林任之的侄女,年纪不大,样貌倒是极美,还未出阁就引得京城王李两家的公子争相提亲", 玉妃将怀里的波斯猫放下,站起身来,端起一盏灯,缓缓向林婉走来。 虽然被发配到浣衣监才几日光景,林婉却也听其他人说起过,在后宫行事千万不要得罪玉妃,当日她好奇追问这是为何,众人却死活不敢说下去, 此刻已是亥时,无论是浣衣监还是凝芳殿都应该早早休息了才对,可偏偏玉妃差人唤自己进殿,过来的一路上,林婉思索了一番,实在想不到自己何时招惹了这尊大佛,只得在心里暗暗祈祷不要出什么事才好。 "本宫倒要看看,这大名鼎鼎的林婉,是不是生的似仙女一般夺人心魄"玉妃轻轻笑了几声,慕然间已然走到了林婉的面前。 "抬起头来"玉妃的声音突然从林婉耳边响起,既不温柔也不冰冷,彷佛没有感情的机器。 林婉乖乖的把头抬起,这才看清玉妃的模样,烛火映照之下,玉妃容色晶莹如玉,如新月生晕,花树堆雪,明眸皓齿,好似九重宫阙的谪仙子一般,难怪在这后宫能如此受宠! 玉妃也在看着她,二八年纪,生得浑然天成的绝美脸庞,朱唇皓齿,亦是美得不可方艳。 两人微微出神,还是林婉先反应过来,连忙低下头,生怕冒犯了玉妃。 玉妃出身高贵,自然眼界不低,何况在这后宫中最不缺的就是各样各式的美女,饶是如此她也不得不承认眼前婢女的容貌称得上祸国殃民。 玉妃深深的瞥了林婉一眼,随即说道,"本宫今夜唤你前来,就是想见识下你的样貌,现在看来,也不过如此,下去吧"。 林婉闻言如释重负,立马起身,顾不得那么许多退出了凝芳殿。 回到浣衣监,林婉还在思索着刚刚发生的事情,她觉得玉妃深夜唤她进宫一定不是看她的容貌这般简单,一夜无眠。 林婉不知道的是,玉妃今夜也失眠了... --------------------------------------------- 我是分割线 -------------------------------------------------------------------------------------------------------------- 次日叫醒林婉的不是公公,而是凝芳殿的婢子秋莹,辰时未至她就到了浣衣监,在门口等了半个时辰, 开门的婢子一来,秋莹就急匆匆的让她带自己去找林婉。 吱呀一声,林婉的房门被推开,此时屋内还是
背景:搭建业务异常的监控告警系统,要求支持两种告警类型
一个是首次告警,好理解,接收到上报的异常就告警
另一个则是统计告警:统计在首次告警后五分钟内该异常出现的总次数
给你五分钟,你会想到什么方式实现统计告警?我想到了定时任务…
其实这是一个典型的延时队列的使用场景
什么是延时队列?
延时队列,首先,它是一种队列,队列意味着内部的元素是有序的,元素出队和入队是有方向性的,元素从一端进入,从另一端取出。
其次,延时队列,最重要的特性就体现在它的延时属性上,跟普通的队列不一样的是,普通队列中的元素总是等着希望被早点取出处理,而延时队列中的元素则是希望被在指定时间得到取出和处理,所以延时队列中的元素是都是带时间属性的,通常来说是需要被处理的消息或者任务。
简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。
延时队列的应用场景
- 在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消。
- 打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单。
- 点外卖的时候,如果商家在10分钟还没接单,就会自动取消订单。
- 收快递的时候,如果我们没有点确认收货,在一段时间后程序会自动完成订单。
- 在平台完成订单后,如果我们没有在规定时间评论商品,会自动默认买家不评论。
延时队列的实现方式
1、DelayQueue 延时队列
JDK
中提供了一组实现延迟队列的API
,位于Java.util.concurrent
包下DelayQueue
。
DelayQueue
是一个BlockingQueue
(无界阻塞)队列,它本质就是封装了一个PriorityQueue
(优先队列),PriorityQueue
内部使用完全二叉堆
(不知道的自行了解哈)来实现队列元素排序,我们在向DelayQueue
队列中添加元素时,会给元素一个Delay
(延迟时间)作为排序条件,队列中最小的元素会优先放在队首。队列中的元素只有到了Delay
时间才允许从队列中取出。队列中可以放基本数据类型或自定义实体类,在存放基本数据类型时,优先队列中元素默认升序排列,自定义实体类就需要我们根据类属性值比较计算了。
先简单实现一下看看效果,添加三个order
入队DelayQueue
,分别设置订单在当前时间的5秒
、10秒
、15秒
后取消。
要实现DelayQueue
延时队列,队中元素要implements
Delayed
接口,这个接口里只有一个getDelay
方法,用于设置延期时间。Order
类中compareTo
方法负责对队列中的元素进行排序。
public class Order implements Delayed {
/**
* 延迟时间
*/
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private long time;
String name;
public Order(String name, long time, TimeUnit unit) {
this.name = name;
this.time = System.currentTimeMillis() + (time > 0 ? unit.toMillis(time) : 0);
}
@Override
public long getDelay(TimeUnit unit) {
return time - System.currentTimeMillis();
}
@Override
public int compareTo(Delayed o) {
Order Order = (Order) o;
long diff = this.time - Order.time;
if (diff <= 0) {
return -1;
} else {
return 1;
}
}
}
DelayQueue
的put
方法是线程安全的,因为put
方法内部使用了ReentrantLock
锁进行线程同步。DelayQueue
还提供了两种出队的方法 poll()
和 take()
, poll()
为非阻塞获取,没有到期的元素直接返回null;take()
阻塞方式获取,没有到期的元素线程将会等待。
public class DelayQueueDemo {
public static void main(String[] args) throws InterruptedException {
Order Order1 = new Order("Order1", 5, TimeUnit.SECONDS);
Order Order2 = new Order("Order2", 10, TimeUnit.SECONDS);
Order Order3 = new Order("Order3", 15, TimeUnit.SECONDS);
DelayQueue<Order> delayQueue = new DelayQueue<>();
delayQueue.put(Order1);
delayQueue.put(Order2);
delayQueue.put(Order3);
System.out.println("订单延迟队列开始时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
while (delayQueue.size() != 0) {
/**
* 取队列头部元素是否过期
*/
Order task = delayQueue.poll();
if (task != null) {
System.out.format("订单:{%s}被取消, 取消时间:{%s}\n", task.name, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
Thread.sleep(1000);
}
}
}
上边只是简单的实现入队与出队的操作,实际开发中会有专门的线程,负责消息的入队与消费。
执行后看到结果如下,Order1
、Order2
、Order3
分别在 5秒
、10秒
、15秒
后被执行,至此就用DelayQueue
实现了延时队列。
订单延迟队列开始时间:2020-05-06 14:59:09
订单:{Order1}被取消, 取消时间:{2020-05-06 14:59:14}
订单:{Order2}被取消, 取消时间:{2020-05-06 14:59:19}
订单:{Order3}被取消, 取消时间:{2020-05-06 14:59:24