商城系统 - 自动取消超时订单解决方案

目的

当订单超过指定时间(毫秒),未支付时,系统自动取消订单。

方案

  • 初步方案

1、定时从数据库获取待取消订单, 放入集合中。
2、将剩余10分钟内待取消订单创建线程,加入(缓存)线程池中,休眠(取消时间-当前时间)后执行取消任务。
3、任务执行完成后移除任务。

缺点:未限制线程数量。如果某个时间段退款过多,会导致创建过多线程。

  • 优化方案1

1、将上述方案缓存线程池,修改为定长线程池。

缺点:运行线程占满时,会导致后续新增的取消订单(退款时间小于休眠中的订单),无法按照指定时间执行。

原因:例如线程池中,可同时执行的线程数为2。当前线程池中,已有两个待取消订单在执行队列中(休眠了执行时间后执行)。导致新增的取消订单任务,需等待前两个中的某一个,执行完成后,才能执行新增的取消订单任务。

  • 最终方案 (如有其他方案、此方案有待改进的地方,欢迎评论一起探讨)

1、定时从数据库获取待取消订单, 放入集合中。将集合按照取消时间,从小到大排序。并通知步骤2。
2、获取集合中,第一个任务的订单取消时间,wait(订单取消时间-当前时间)后将线程将线程加入线程池中,执行取消订单任务。无任务时wait()。
3、任务执行完成后移除任务。

简易流程图.png

实现代码 (待删除无用代码)

package com.gtmc.uccs.common.util;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import lombok.extern.slf4j.Slf4j;

/**
 * 定时处理工具类
 * 
 * @description: 定时处理工具类
 * @author: chenfei
 * @create: 2020-12-23 16:21:55
 */
@Slf4j
public abstract class TimingProcess {
  
  /**
   * 执行任务的线程池
   */
  private final ExecutorService taskExecuteThreadPool;
  
  /**
   * 待执行的任务队列 
   */
  private final List> taskQueue = Collections.synchronizedList(new LinkedList>());
  
  /**
   * 执行中的任务队列
   */
  private final List> readiedTaskQueue = Collections.synchronizedList(new LinkedList>());
  
  /**
   * 调度程序占用的线程数
   */
  private final int processTread = 2;
  
  /**
   * 循环调用间隔
   */
  private int sleepTime;
  
  private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
  
  /**
   * 创建定时处理程序 立即执行当前可执行方法 并在指定时间循环调用
   * 
   * @param sleepTime 循环调用间隔时间 单位:分
   * @author: chenfei
   * @create: 2020-12-24 13:19:01
   */
  public TimingProcess(int sleepTime) {
    this(sleepTime, 5);
  }
  
  /**
   * 创建定时处理程序 立即执行当前可执行方法 并在指定时间循环调用
   * 
   * @param sleepTime 循环调用间隔时间 单位:分
   * @author: chenfei
   * @create: 2020-12-24 13:19:01
   */
  public TimingProcess(int sleepTime, int maxTaskExecuteNum) {
    this.sleepTime = sleepTime;
    taskExecuteThreadPool = Executors.newFixedThreadPool(maxTaskExecuteNum+processTread);
    this.executeTask();
    createThreadRunDispatchTask(sleepTime);
  }

  /**
   * 创建一个线程 根据循环调用调度任务
   * 
   * @description: 根据指定时间循环调用调度任务
   * @param sleepTime 间隔多长时间循环调用
   * @author: chenfei
   * @create: 2020-12-24 12:50:22
   */
  private void createThreadRunDispatchTask(int sleepTime) {
    taskExecuteThreadPool.execute((new Runnable() {

      @Override
      public void run() {
       while(true) {
         dispatchTaskNow();
         try {
           Thread.sleep(sleepTime*60*1000);
         } catch (InterruptedException e) {
           log.error("休眠被异常打断", e);
         }
       }
      }
      
    }));
  }
  
  /**
   * 执行调用任务 循环扫描队列 将可执行任务创建线程进行定时处理
   * 
   * @description: 执行调用任务 循环扫描队列 将可执行任务创建线程进行定时处理
   * @author: chenfei
   * @create: 2020-12-24 13:01:44
   */
  private synchronized void dispatchTaskNow() {
    this.sortTaskQueue();
    log.debug("任务调度 开始执行 ... 当前队列等待执行任务: {}个 预备中的任务: {}", this.taskQueue.size(), readiedTaskQueue.size());
    Iterator> iterator = this.taskQueue.iterator();
    while(iterator.hasNext()) {
      Task task = iterator.next();
      Date taskRunTime = task.getTaskRunTime();
      // 如果执行日期小于定时循环时间 创建任务并加入到执行中的队列
      Calendar calendar = Calendar.getInstance();
      calendar.add(Calendar.MINUTE, sleepTime);
      if(calendar.getTime().compareTo(taskRunTime) >= 0) {
        readiedTaskQueue.add(task);
        synchronized (readiedTaskQueue) {
          readiedTaskQueue.notify();
        }
      }
    }
    this.taskQueue.removeAll(readiedTaskQueue);
    log.debug("任务调度 执行结束 ... 当前队列等待执行任务: {}个 预备中的任务: {}", this.taskQueue.size(), readiedTaskQueue.size());
  }
  
  /**
   * 执行任务
   * 
1)当前预备队列中有可执行任务时,取时间最小的作为等待时间,等待时间结束时开始处理任务。 *
2) 当前预备队列无可执行任务时,一直等待直至被唤醒 * * * @description: 执行任务 * @author: chenfei * @create: 2020-12-24 23:38:57 */ private void executeTask() { Thread thread = new Thread(new Runnable() { @Override public void run() { while(true) { synchronized (readiedTaskQueue) { // 排序预备中的任务 Collections.sort(readiedTaskQueue); try { if(readiedTaskQueue.size()<=0) { readiedTaskQueue.wait(); } Task task = readiedTaskQueue.get(0); Date taskRunTime = task.getTaskRunTime(); // 第一次判断需要睡眠多久 long sleepTime = taskRunTime.getTime()-System.currentTimeMillis(); if(sleepTime>0) { readiedTaskQueue.wait(sleepTime); } // 第二次获取时间 判断当前是否可执行人 有可能是被调度器唤醒 非等待时间结束 sleepTime = taskRunTime.getTime()-System.currentTimeMillis(); if(sleepTime<=0) { taskExecuteThreadPool.execute(taskToThread(task)); readiedTaskQueue.remove(task); } } catch (InterruptedException e) { log.error("线程等待被异常唤醒", e); } } } } }); taskExecuteThreadPool.execute(thread); } /** * 创建一个线程执行任务内容 * * @description: 创建一个线程执行任务内容 * @param t * @param date 定时处理的时间 * @return * @author: chenfei * @create: 2020-12-24 13:02:45 */ private Runnable taskToThread(final Task task) { return new Runnable() { @Override public void run() { try { log.debug("任务定时执行时间: {} 当前系统时间: {}",SIMPLE_DATE_FORMAT.format(task.getTaskRunTime()), SIMPLE_DATE_FORMAT.format(new Date())); handle(task); }finally { log.debug("{} 任务执行结束 即将从队列中移除", task); readiedTaskQueue.remove(task); } } }; } /** * 任务执行的内容 * * @description: 需执行的任务 * * @param task 任务 * @author: chenfei * @create: 2020-12-23 17:34:19 */ public abstract void handle(Task task); /** * 添加一个任务到队列 当前有可执行任务时,会立即进入执行队列。 其余进去待调度队列 * @description: 添加一个任务到队列 * @param task 任务 * @author: chenfei * @create: 2020-12-24 21:26:36 */ public void addTask(Task task) { if(task == null) { return; } synchronized (this) { if(readiedTaskQueue.contains(task) || taskQueue.contains(task)) { return; } taskQueue.add(task); } dispatchTaskNow(); } /** * 添加全部任务 * * @description: 添加全部任务 * @param tasks 任务集合 * @author: chenfei * @create: 2020-12-24 15:36:02 */ public void addAllTask(List> tasks) { if(tasks == null) { return; } synchronized (this) { for (Task task : tasks) { if(readiedTaskQueue.contains(task) || taskQueue.contains(task)) { return; } taskQueue.add(task); } this.sortTaskQueue(); } dispatchTaskNow(); } private void sortTaskQueue() { Collections.sort(this.taskQueue); } /** * 任务类 * * @program: common-core * @description: 任务类 * @author: chenfei * @create: 2020-12-24 21:53:58 */ public static class Task implements Comparable>{ /** * 执行任务所需的参数对象 */ private T taskParam; /** * 任务运行时间 */ private Date taskRunTime; public Task(T taskParam, Date taskRunTime) { super(); this.taskParam = taskParam; this.taskRunTime = taskRunTime; } public T getTaskParam() { return taskParam; } public void setTaskParam(T taskParam) { this.taskParam = taskParam; } public Date getTaskRunTime() { return taskRunTime; } @Override public int compareTo(Task task) { return this.getTaskRunTime().compareTo(task.getTaskRunTime()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((taskParam == null) ? 0 : taskParam.hashCode()); result = prime * result + ((taskRunTime == null) ? 0 : taskRunTime.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; @SuppressWarnings("unchecked") Task other = (Task) obj; if (taskParam == null) { if (other.taskParam != null) return false; } else if (!taskParam.equals(other.taskParam)) return false; if (taskRunTime == null) { if (other.taskRunTime != null) return false; } else if (!taskRunTime.equals(other.taskRunTime)) return false; return true; } } }

你可能感兴趣的:(商城系统 - 自动取消超时订单解决方案)