前言:说一下这里做的订单到期,用户还未支付,对订单进行失效处理
线上总共有4台机。有一台是job机,再说下,我们日常商城订单量并不算多,也就几百单, 所以这里是一个简单的订单失效处理
public class App2 {
private final static Logger LOGGER = LoggerFactory.getLogger(App2.class);
private final LinkedBlockingQueue queue = new LinkedBlockingQueue();
private final Timer timer = new Timer("ScheduleOrderOutTimerThread");
/**
* 首次处理的的状态标识
*/
private AtomicBoolean delayedFlag = new AtomicBoolean(false);
private static final long DELAY_FOR_A_PERIOD = 4000L;
/**
* 描述:处理过期的订单
*
* @return
* @author: miaomiao
* @date: 2019-05-21 10:37
*/
@Override
public JSONObject handleOutTimeOrder(String params) {
JSONObject json = new JSONObject();
json.put("result", "ok");
json.put("msg", "调用成功");
//订单超时时间,这个时间是限制用户停止支付
Integer outTime = config.getShopTimeOutMinute();
//过期多久时间内置为取消,时间单位是分钟,这个是订单停止支付,多少时间之后置为失效,怕用户这段时间期间内再第三方进行订单支付
Integer min = config.getOutTimeRangeCancel();
OrderBy orderBy = new OrderBy("create_time", OrderBy.ASC);
ShopOrderQuery query = new ShopOrderQuery();
query.setIncludeStatus(Arrays.asList(ShopOrderStatusEnum.WAIT_PAY.getStatus()));
query.setOrderBy(orderBy);
//根据时间正序查询 用户未支付的订单
List orderList = shopOrderService.query(query);
if (CollectionUtils.isNotEmpty(orderList)) {
for (ShopOrder shopOrder : orderList) {
try {
queue.put(shopOrder);
} catch (InterruptedException e) {
LOGGER.error("放入queue异常", e);
}
}
//delayedFlag 的作用,限制只有一个线程在进行队列里订单的失效处理
if (!delayedFlag.get()) {
//取 队列的头部 订单,第一个订单是最早的订单
ShopOrder shopOrder = queue.poll();
if (shopOrder != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(shopOrder.getCreateTime());
cal.add(Calendar.MINUTE, outTime + min);
/**
* 一个定时线程去延时启动,执行失效处理
* 所以这里有个可能,这个订单用户正在进行支付
* 所以这里是当前时间+ (之前配置文件里取出来的 outTime) 过期时间。
*/
this.timer.schedule(new DeliverDelayedMessageTimerTask(shopOrder, 0), cal.getTime());
delayedFlag.compareAndSet(false, true);
} else {
delayedFlag.compareAndSet(true, false);
json.put("msg", "暂无订单数据");
}
}
} else {
json.put("result", "ok");
json.put("msg", "暂无订单数据");
}
return json;
}
class DeliverDelayedMessageTimerTask extends TimerTask {
private ShopOrder shopOrder;
/**
* 一次任务的失败次数
*/
private AtomicInteger errorNums;
public DeliverDelayedMessageTimerTask(ShopOrder shopOrder, int errorNums) {
this.shopOrder = shopOrder;
this.errorNums = new AtomicInteger(errorNums);
}
@Override
public void run() {
try {
this.executeOnTimeup();
} catch (Exception e) {
LOGGER.error("ScheduleMessageService, executeOnTimeup exception,prams:" + shopOrder.toString(), e);
//默认重试5次
if (errorNums.incrementAndGet() < 5) {
ShopOrderDelayManagerImpl.this.timer.schedule(new DeliverDelayedMessageTimerTask(this.shopOrder, errorNums.get()), DELAY_FOR_A_PERIOD);
} else {
delayedFlag.compareAndSet(true, false);
}
}
}
public void executeOnTimeup() throws Exception {
while (true) {
LOGGER.info("shopOrder进行消息处理:"+this.shopOrder);
if (this.shopOrder == null) {
if(take()){
continue;
}else{
break;
}
}
//去判断订单的状态,如果订单不是等待状态 || 处理中状态,则拿取下一个订单。
if(this.shopOrder.getStatus() != ShopOrderStatusEnum.WAIT_PAY.getStatus() &&
this.shopOrder.getStatus() !=ShopOrderStatusEnum.PROCESS_PAY.getStatus() ){
if(take()){
continue;
}else{
//退出本次线程
break;
}
}
//1。去第三方核对订单是否支付
//2、回滚份额是首步,就算失败了,继续延迟,然后更新订单
//3、回滚积分
//4、回滚订单
this.shopOrder =null;
}
}
/**
* 拿取商品
* @return
*/
private boolean take( ){
//过期多久时间内置为取消,时间单位是分钟
Integer min = config.getOutTimeRangeCancel();
//订单超时时间
Integer outTime = config.getShopTimeOutMinute();
ShopOrder shopOrder = queue.poll();
if (shopOrder != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(shopOrder.getCreateTime());
cal.add(Calendar.MINUTE, outTime + min);
//现在的时间 > 订单超过处理时间
if(System.currentTimeMillis()>cal.getTimeInMillis() ){
this.shopOrder = shopOrder;
this.errorNums = new AtomicInteger(0);
return true;
} else{
ShopOrderDelayManagerImpl.this.timer.schedule(new DeliverDelayedMessageTimerTask(shopOrder, 0), cal.getTime());
return false;
}
} else {
delayedFlag.compareAndSet(true, false);
return false;
}
}
}
}
1、一台机job去定时跑逾期订单
2、会捞取待付款的订单,把订单放入 LinkedBlockingQueue
3、delayedFlag代表只有一个线程去处理逾期订单, delayedFlag:true,已经有订单在处理中,false代表线程空闲,支持开启一个线程去处理
4、然后会启动一个延时线程去处理该订单,延时时间设置为:当前时间+订单过期时间+过期延迟时间
5、去处理订单的时候会有个时间判断,判断是否到达时间去处理订单。时间未达到的话,会启动一个延时线程去处理,然后结束本线程。时间达到的话,就直接处理该订单
6、判断订单是否处于未支付状态
7、判断订单在第三方是否付款
8、处理订单逾期业务
9、把this.shopOrder置为null、再次走循环