1、利用延迟队列
延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取任务否则获取不到……
应用场景比较多,比如延时1分钟发短信,延时1分钟再次执行等,下面先看看延时队列demo之后再看延时队列在项目中的使用:
/**
*延迟队列消息体
*消息体定义 实现Delayed接口就是实现两个方法即compareTo 和 getDelay最重要的就是getDelay 方法,
这个方法用来判断是否到期…… */
*/
@Log4j
@Data
public class DelayMessage implements Delayed {
private Long orderId;
/**
* 运行日期,转化成毫秒值 延迟时长,这个是必须的属性因为要按照这个判断延时时长。
*/
private long executeTime;
private int times;
private DelayMessage() {}
public DelayMessage(Long orderId, long executeTime) {
this(orderId,executeTime,0);
}
public DelayMessage(Long orderId, long executeTime, int times){
this.orderId = orderId;
this.executeTime = executeTime;
this.times = times;
}
@Override
public int compareTo(Delayed o) {
DelayMessage dm = (DelayMessage) o;
return dm.getExecuteTime() > this.executeTime ? -1 : (dm.getExecuteTime() == this.executeTime ? 0 : 1);
}
/**
* 单位:毫秒
*/
@Override
public long getDelay(TimeUnit unit) {
try {
// 0.5秒检查一次
Thread.sleep(500);
} catch (InterruptedException e) {
log.error("getDelay Interrupted",e);
Thread.currentThread().interrupt();
}
return executeTime - System.currentTimeMillis();
}
}
########################################################
/**
*
* @author
*/
@Component
public class DelayQueueExecutor {
@Autowired
private OrderSignCountdownCache orderSignCountdownCache;
@Autowired
private OrderService orderService;
@Autowired
private OrderTransactionService orderTransactionService;
/**
* 全局延时队列
*/
private static DelayQueue<DelayMessage> queue = new DelayQueue<DelayMessage>();
/**
* 延时队列 添加数据
*
* @param orderId 订单号
* @param timeOut 触发时间
*/
public static void putDelayQueue(Long orderId,Long timeOut) {
queue.put(new DelayMessage(orderId , timeOut));
}
/**
* 延时队列 添加数据
*
* @param orderId 订单号
* @param timeOut 触发时间
*/
static void putDelayQueue(Long orderId,Long timeOut,int times) {
queue.put(new DelayMessage(orderId , timeOut));
}
/**
* 启动延时队列消费者
*/
@PostConstruct
private void initThread() {
Function<Long,Object> task = new Function<Long,Object> () {
@Override
public Object apply(Long orderId) {
Order order = orderService.getOrderById(orderId, null);
if(EnumOrderStatus.WAIT_SIGN.getKey().equals(order.getOrderStatus())){
return orderTransactionService.doUpdateOrderStatusToCancel("超期未签章",order);
}
return null;
}
};
new Thread(new MessageConsumer(queue,orderSignCountdownCache,task)).start();
new Thread(new MessageConsumer(queue,orderSignCountdownCache,task)).start();
}
}
########################################################
/**
* @author
*/
public interface Function<T, R> {
/**
* Function
* @see "java.util.function.Function"
* @param t t
* @return R
*/
R apply(T t);
}
########################################################
/**
* @author
*/
@Slf4j
public class MessageConsumer implements Runnable {
/**
* 延时队列 ,消费者从其中获取消息进行消费
*/
private DelayQueue<DelayMessage> queue;
private OrderSignCountdownCache orderSignCountdownCache;
private Function<Long,Object> task;
MessageConsumer(DelayQueue<DelayMessage> queue, OrderSignCountdownCache orderSignCountdownCache, Function<Long,Object> task) {
this.queue = queue;
this.orderSignCountdownCache = orderSignCountdownCache;
this.task = task;
}
@Override
public void run() {
DelayMessage msg = null;
while (true) {
try {
msg = queue.take();
// 可以调整为异步调用
Long orderId = msg.getOrderId();
boolean isSuccess = orderSignCountdownCache.del(orderId);
if(isSuccess) {
log.info("抢数据 success:" + orderId);
// 取消订单业务代码
task.apply(orderId);
}
} catch (InterruptedException e) {
if(msg != null){
log.error("消息消息失败,param={} ,e = {}" , JSONObject.toJSONString(msg),e);
}else{
log.error("消息消息失败:" ,e);
}
Thread.currentThread().interrupt();
}
}
}
}