延时队列,是一种有定时器的队列。比如商品订单支付失效,电影票锁座时间等,订单需要经过一段时间没有支付时要变为失效状态。延时队列的实现各种各样,比如JDK自带的DelayQueue,Redis、消息队列等,以下详细描述。

实现DelayQueue接口

实现java.util.concurrent.Delayed接口中的getDelay方法和compareTo方法,前者为了获取剩余失效时间,后者用来比较谁先超时。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Data
public class Order implements Delayed {

private String id;
private String name;
/** 创建时间*/
private Long createTime;
/** 持续时间*/
private Long time;
/** 结束时间*/
private Long endTime;

public Order(){}
public Order(String name, Long time, TimeUnit timeUnit) {
this.id= IdUtil.fastUUID();
this.name = name;
this.createTime = System.currentTimeMillis();
this.time = this.createTime+timeUnit.toMillis(time);
this.endTime = -1L;
}
/** 获取剩余的时间*/
@Override
public long getDelay(TimeUnit unit) {
return this.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;
}
}
}

使用的主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UseMain {
public static void main(String[] args) {
Order Order1 = new Order("Order1", 5L, TimeUnit.SECONDS);
Order Order2 = new Order("Order2", 10L, TimeUnit.SECONDS);
Order Order3 = new Order("Order3", 15L, 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.getName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
ThreadUtil.sleep(1000);
}
}
}

Redis的zset数据结构

zset有序集合,和普通的集合一样,有string类型的成员,但是每个成员关联也给double类型的分数,成员不能重复,redis可以根据分数进行排序。

命令 描述
ZADD KEY SCORE1 MEMBER1 [] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZCARD KEY 获取有序集合中的成员数
ZCOUNT KEY min max 计算在有区间分数的元素数
ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment
ZINTERSTORE destination numkeys key 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中
ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量
ZRANGE key start stop 通过索引区间返回有序集合指定区间内的成员
ZRANGEBYSCORE key min max WITHSCORES [LIMIT] 通过分数返回有序集合指定区间内的成员
zrank key member 返回有序集合中指定成员的索引
zrem key member 移除指定成员
ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员
ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员
ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到低
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序

思路:主要使用zadd添加订单和失效时间和zrangebyscore,轮询获取最近的失效时间,与当前时间进行比较,看是否失效。失效之后移除。

Redis 过期回调

RabbitMQ 延时队列

时间轮