一、使用場景
1、不需要立即執行、立即得到結果返回。
2、如果執行失敗、需要有失敗補償機制。
3、和業務程式碼解耦,適用於不同的務場景。
4、呼叫介面的入參、出參 統計,方便查詢。
二、執行順序
1、業務邏輯中,需要呼叫外部介面時,將引數組裝好,往任務表中插入一條任務記錄。(主要包括 任務型別、需要執行的類、方法、引數 等)
2、使用定時任務(xxlJob或分散式worker)定時掃描任務表中待執行或執行失敗(未超過最大重試次數)的任務。
3、拿到待執行任務後,採用反射思想 執行任務,並記錄執行狀態和執行結果。
三、程式碼示例
表設計(通用任務執行表)
主要欄位
任務型別 、task_type
執行狀態、exec_status(待執行、執行成功、執行失敗)
執行的類、exec_class
執行的方法、exec_method
執行方法的引數、exec_param
執行結果、exec_result
重試次數、retry_times
核心程式碼
定時任務排程
/** * 執行通用任務 */ public void doTaskList() { List<Task> taskList = taskMapper.selectTaskList(); if (CollectionUtils.isEmpty(taskList)) { return; } for (Task task : taskList) { try { Integer retryTimes = task.getRetryTimes(); if (retryTimes == 1) { Date updateTime = task.getGmtModified(); // 第一次重試,執行時間和上次時間間隔至少5分鐘 if (updateTime.getTime() + 1000 * 60 * 5 > System.currentTimeMillis()) { continue; } } if (retryTimes == 2) { Date updateTime = task.getGmtModified(); // 第二次重試,執行時間和上次時間間隔至少30分鐘 if (updateTime.getTime() + 1000 * 60 * 30 > System.currentTimeMillis()) { continue; } } service.doTaskExec(task); } catch (Exception e) { // 執行失敗傳送提醒郵件 } } }
反射執行
/** * 通用任務執行 * * @param task 待執行的任務 */ public void doTaskExec(Task task) throws ClassNotFoundException { String execClass = task.getExecClass(); String execMethod = task.getExecMethod(); String execParam = task.getExecParam(); Class<?> clazz = Class.forName(execClass); Object object = ApplicationContextUtil.getBean(clazz); Method[] methods = clazz.getMethods(); for (Method method : methods) { if (!method.getName().equals(execMethod)) { continue; } Class<?>[] paramTypes = method.getParameterTypes(); Object[] objectValue = new Object[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { objectValue[i] = JSON.parseObject(execParam, paramTypes[i]); } Object execResult; try { execResult = reflection(object, clazz, execMethod, paramTypes, objectValue); } catch (Exception e) { log.error("外部介面返回異常:", e); processFailureExecResult(task, e.getMessage()); return; } processExecResult(task, JSON.toJSONString(execResult)); } }