在电商订单处理场景中,需要把超时的订单进行关闭,可使用多种方式处理超时订单:
- 使用数据库定时任务,每隔几秒扫描订单表,找出超时订单后关闭。
- 使用spring的@Scheduled注解启动定时任务或者使用Quartz任务管理器,定时触发任务,处理超时订单。
- 使用消息中间件,ActiveMQ或者RocketMQ 都提供了延迟消息队列,下单后往延迟消息队列中发消息,超时后,消费端会接收到一条延迟的订单消息,并做相应处理。
- 在用户查询的时候,检测订单是否超时,若超时,则关闭订单
- 使用DelayQueue来实现。
- 其他方式
今天我要说的是使用DelayQueue自己实现一个订单超时关闭的解决方案,先贴实现代码:
@Component
@Lazy(false)
public class DelayOrderComponent{
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderService orderService;
private static DelayQueue delayQueue = new DelayQueue();
@PostConstruct
public void init() throws Exception {
/**初始化时加载数据库中需处理超时的订单**/
List orderList = orderMapper.selectOverTimeOrder();
for (int i = 0; i < orderList.size(); i++) {
OrderMessage orderMessage = new OrderMessage(propertyCarList.get(i).getOrderId(),propertyCarList.get(i).getCreateTime());
this.addToOrderDelayQueue(orderMessage);
}
/**启动一个线程,去取延迟消息**/
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
OrderMessage message = null;
while (true) {
try {
message = delayQueue.take();
//处理超时订单
orderService.closeOverTimeOrder(message.getOrderId());
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
/**加入延迟消息队列**/
private boolean addToOrderDelayQueue(OrderMessage orderMessage){
return delayQueue.add(orderMessage);
}
/**从延迟队列中移除**/
private void removeToOrderDelayQueue(OrderMessage orderMessage){
if(Tools.isEmpty(orderMessage)){
return;
}
for (Iterator iterator = delayQueue.iterator(); iterator.hasNext();) {
OrderMessage queue = (OrderMessage) iterator.next();
if(orderMessage.getOrderId().equals(queue.getOrderId())){
delayQueue.remove(queue);
}
}
}
}
- init()方法:在对象实例化的时候,加载数据库中的需要延时处理的订单,并启动一个线程等待处理超时出队列的订单。
- 对外提供加入消息到延迟队列方法 addToOrderDelayQueue ,当下单的时候加入延迟消息队列。
- 对外提供删除延迟消息方法 removeToOrderDelayQueue ,当用户主动取消订单,或者支付成功后使用。
public class OrderMessage implements Delayed {
private final static long DELAY = 15*60*1000L;//默认延迟15分钟
private final String orderId;//订单号
private final long startTime ;//开始时间
private final long expire ;//到期时间
private final Date now; //创建时间
private final String orderMsg;//订单其他信息JSON方式保存,备用字段
public OrderMessage(String orderId, String startTimeStr14 ,long secondsDelay) {
super();
this.orderId = orderId;
this.startTime = DateUtils.toDateTime14(startTimeStr14).getTime();
this.expire = startTime + (secondsDelay*1000);
this.now = new Date();
this.orderMsg="";
}
public OrderMessage(String orderId, String startTimeStr14, String orderMsg ,long secondsDelay) {
super();
this.orderId = orderId;
this.startTime = DateUtils.toDateTime14(startTimeStr14).getTime();
this.expire = startTime + (secondsDelay*1000);
this.orderMsg = orderMsg;
this.now = new Date();
}
public OrderMessage(String orderId, String startTimeStr14) {
super();
this.orderId = orderId;
this.startTime = DateUtils.toDateTime14(startTimeStr14).getTime();
this.expire = startTime + DELAY;
this.now = new Date();
this.orderMsg="";
}
public OrderMessage(String orderId, String startTimeStr14, String orderMsg) {
super();
this.orderId = orderId;
this.startTime = DateUtils.toDateTime14(startTimeStr14).getTime();
this.expire = startTime + DELAY;
this.orderMsg = orderMsg;
this.now = new Date();
}
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) -o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis() , TimeUnit.MILLISECONDS);
}
public String getOrderId() {
return orderId;
}
public String getOrderMsg() {
return orderMsg;
}
public Date getNow() {
return now;
}
public long getStartTime() {
return startTime;
}
public long getExpire() {
return expire;
}
}
- 延迟消息必须实现 Delayed 接口,并实现compareTo和getDelay接口。
- 此处我定义了多个构造方法,方便在多种场景下使用。
public static void main(String[] args) {
final DelayQueue delayQueue = new DelayQueue();
delayQueue.add(new OrderMessage(""+1, DateUtils.curDateTimeStr14(),"3秒后执行"));
delayQueue.add(new OrderMessage(""+2, DateUtils.curDateTimeStr14(),"4秒后执行"));
delayQueue.add(new OrderMessage(""+3, DateUtils.curDateTimeStr14(),"8秒后执行"));
/**启动一个线程,处理延迟消息**/
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
OrderMessage message = null;
while (true) {
try {
message = delayQueue.take();
System.out.println(new Date()+" 处理延迟消息: "+JSON.toJSONString(message));
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
如上是简单测试代码。
个人经验,有更好办法,欢迎在评论区补充,大家一起进步!!!