任務需求:
關閉超時未支付的訂單,將訂單信息置為失效狀態
相關技術:
quartz框架定時調度
實現思路:
- 在服務啟動時,查詢數據庫中的已下單未支付的訂單數據,按下單時間先后存入隊列中,先下單的存到頭不,后下單存入隊列尾部,取隊列的頭元素
- 檢測與現在的時間,如果超過40分鍾,則執行數據操作,即關閉訂單,但是只關閉未支付的訂單,之后在將頭元素從隊列移出,並取出下一個元素進行檢測,以此類推
- 如果檢測出時間未到40分鍾,則線程等待相應的時間差,之后在執行訂單操作
相關問題:
- 在執行時要防止輪詢任務追尾,即在上一個job未執行完畢時就開始下一次輪詢,解決方法是在job上加@DisallowConcurrentExecution注解,該注解的作用是讓下一次job要等待當前job執行完畢
- 設置的輪詢間隔是35分鍾一次,訂單超時是40分鍾,中間有5分鍾的時間差,為了防止訂單被多次加入隊列中,在加入訂單隊列時要注意去重
相關代碼
package com.ichunshen.dolook.module.trade.order.support; import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import org.apache.log4j.Logger; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; import com.ichunshen.dolook.module.trade.order.job.CancelOrderJob; import com.ichunshen.dolook.module.trade.order.model.Order; import com.ichunshen.dolook.module.trade.order.web.OrderController; public class CancelOrderTask { static Logger logger = Logger.getLogger(OrderController.class); public void cancelOrderTask() throws SchedulerException { // 獲得一個scheduler SchedulerFactory sh = new StdSchedulerFactory(); Scheduler scheduler = sh.getScheduler(); // 創建一個job 任務名,任務組,任務執行類 JobDetail job = newJob(CancelOrderJob.class).withIdentity("cancelOrderJob", "orderJob").build(); //創建一個觸發器 CronTrigger trigger = newTrigger().withIdentity("cancelOrderTrigger", "orderTrigger") .withSchedule(cronSchedule("0 0/2 * * * ?")).build(); //將job和觸發器綁定 Date date=scheduler.scheduleJob(job, trigger); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); logger.info(job.getKey()+"取消訂單定時任務於"+sdf.format(date)+"開始執行"); scheduler.start(); } }
package com.ichunshen.dolook.module.trade.order.job; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import org.apache.log4j.Logger; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; import org.quartz.StatefulJob; import com.ichunshen.dolook.module.trade.order.model.Order; import com.ichunshen.dolook.module.trade.order.service.OrderService; import com.ichunshen.dolook.module.trade.order.support.OrderQueue; import com.ichunshen.dolook.support.DoLookConstant.OrderCancelMethod; import cn.joy.framework.plugin.quartz.ScheduleTask; /** * quartz任務的job,用於檢測數據庫失效訂單並將其關閉 * @author wangpeiqing * */ @DisallowConcurrentExecution public class CancelOrderJob implements ScheduleTask { Logger logger=Logger.getLogger(CancelOrderJob.class); @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { // TODO Auto-generated method stub System.out.println("失效訂單檢測任務開始執行!"); Order order =new Order(); OrderQueue queue = new OrderQueue(); //在每次啟動Job時去數據庫查找失效訂單,並加入到隊列中 List<Order> list=order.getInvalidOrder(); if(!list.isEmpty()){ for (Order o : list) { queue.offer(o); } } //獲取隊列的頭元素,開始檢測頭訂單是否失效 Order element=queue.peek(); while (element!=null) { Long time=this.checkOrder(element); if (time != null && time >=2400*1000) { System.out.println("開始關閉訂單"+element.getOcode()+"下單時間"+element.getOrderTime()); element.cancelInvalidOrderStatus(element.getOcode(), OrderCancelMethod.INVALID_TIME); queue.poll(); element=queue.peek(); }else if(time<2400*1000){ try { System.out.println("等待檢測訂單"+element.getOcode()+"下單時間"+element.getOrderTime()+"已下單"+time/1000+"秒"); Thread.sleep(time); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); logger.info("CancelOrderJob.checkOrder定時任務出現問題"); } } } } /** * 獲取訂單的下單時間和現在的時間差 * @author wangpeiqing * 2016年4月16日 * @param order * @return * */ public Long checkOrder(Order order) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); OrderQueue queue = new OrderQueue(); Long diff = null; if (order != null) { Date orderTime = order.getOrderTime(); try { diff = sdf.parse(sdf.format(date)).getTime() - sdf.parse(sdf.format(orderTime)).getTime(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //返回值為毫秒 return diff; } }
package com.ichunshen.dolook.module.trade.order.support; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.quartz.SchedulerException; public class CancelOrderListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // TODO Auto-generated method stub CancelOrderTask task = new CancelOrderTask(); try { task.cancelOrderTask(); } catch (SchedulerException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void contextDestroyed(ServletContextEvent sce) { // TODO Auto-generated method stub } }
