在开发中,往往会遇到一些关于延时任务的需求。例如
- 生成订单30分钟未支付,则自动取消
- 生成订单60秒后,给用户发短信
对上述的任务,我们给一个专业的名字来形容,那就是延时任务。那么这里就会产生一个问题,这个延时任务和定时任务的区别究竟在哪里呢?一共有如下几点区别:
定时任务有明确的触发时间,延时任务没有
定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务
解决方案:
- 1、JDK的延迟队列
- 2、时间轮算法--HashedWheelTimer
- 3、Redisson延迟队列RDelayedQueue
前俩方案的优缺点:
- 优点:
效率高,任务触发时间延迟时间比delayQueue低,代码复杂度比delayQueue低。 - 缺点:
(1)服务器重启后,数据全部消失,怕宕机
(2)集群扩展相当麻烦
(3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常
使用Redisson延迟队列RDelayedQueue
1、 pom.xml
<!-- JDK 1.8+ compatible -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.2</version>
</dependency>
<!-- JDK 1.6+ compatible -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.10.4</version>
</dependency>
2、队列中要存入的元素实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employer {
private String name;
private int age;
private String wife;
private Double salary;
private String putTime;
public void setPutTime() {
this.putTime = new SimpleDateFormat("hh:mm:ss").format(new Date());
}
}
3、生成订单并放进延时队列的类
package com.redisson;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class RedisPutInQueue {
public static void main(String args[]) {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
RBlockingQueue<Employer> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue");
RDelayedQueue<Employer> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
for (int i = 0; i < 10; i++) {
try {
//模拟间隔投递消息
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 一分钟以后将消息发送到指定队列
//相当于1分钟后取消订单
//延迟队列包含callCdr 1分钟,然后将其传输到blockingFairQueue中
//在1分钟后就可以在blockingFairQueue 中获取callCdr了
Employer callCdr = new Employer();
callCdr.setSalary(345.6);
callCdr.setPutTime();
delayedQueue.offer(callCdr, 1, TimeUnit.MINUTES);
System.out.println("callCdr =================================> " + callCdr);
}
//在该对象不再需要的情况下,应该主动销毁。
// 仅在相关的Redisson对象也需要关闭的时候可以不用主动销毁。
delayedQueue.destroy();
//redissonClient.shutdown();
}
}
4、取消订单的操作类
package com.redisson;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.text.SimpleDateFormat;
import java.util.Date;
public class RedisOutFromQueue {
public static void main(String args[]) {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
RBlockingQueue<Employer> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue");
RDelayedQueue<Employer> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
while (true) {
Employer callCdr = null;
try {
callCdr = blockingFairQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单取消时间:" + new SimpleDateFormat("hh:mm:ss").format(new Date()) + "==订单生成时间" + callCdr.getPutTime());
}
}
}
参考: