Commit 5fc03bd8aa6201ffc3d08459b48ce12a57b389e0

Authored by YunaiV
1 parent f60855fa

完善 JobHandlerInvoker 的执行,记录 Job 执行日志

src/main/java/cn/iocoder/dashboard/framework/quartz/core/handler/JobHandlerInvoker.java
1 1 package cn.iocoder.dashboard.framework.quartz.core.handler;
2 2  
  3 +import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.NumberUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.dashboard.common.exception.ServiceException;
  8 +import cn.iocoder.dashboard.common.pojo.CommonResult;
3 9 import cn.iocoder.dashboard.framework.quartz.core.enums.JobDataKeyEnum;
  10 +import cn.iocoder.dashboard.framework.quartz.core.service.JobLogFrameworkService;
  11 +import lombok.extern.slf4j.Slf4j;
4 12 import org.quartz.DisallowConcurrentExecution;
5 13 import org.quartz.JobExecutionContext;
  14 +import org.quartz.JobExecutionException;
6 15 import org.quartz.PersistJobDataAfterExecution;
7 16 import org.springframework.context.ApplicationContext;
8 17 import org.springframework.scheduling.quartz.QuartzJobBean;
9 18  
10 19 import javax.annotation.Resource;
  20 +import java.util.Date;
  21 +
  22 +import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
  23 +import static cn.iocoder.dashboard.util.date.DateUtils.diff;
11 24  
12 25 /**
13 26 * 基础 Job 调用者,负责调用 {@link JobHandler#execute(String)} 执行任务
14 27 *
15 28 * @author 芋道源码
16 29 */
17   -@PersistJobDataAfterExecution
18 30 @DisallowConcurrentExecution
  31 +@PersistJobDataAfterExecution
  32 +@Slf4j
19 33 public class JobHandlerInvoker extends QuartzJobBean {
20 34  
21 35 @Resource
22 36 private ApplicationContext applicationContext;
23 37  
  38 + @Resource
  39 + private JobLogFrameworkService jobLogFrameworkService;
  40 +
24 41 @Override
25   - protected void executeInternal(JobExecutionContext executionContext) {
26   - // 获得 JobHandler 对象
  42 + protected void executeInternal(JobExecutionContext executionContext) throws JobExecutionException {
  43 + // 获得 Job 数据
  44 + // 1. 获得 jobId 参数
  45 + String jobIdStr = getJobData(executionContext, JobDataKeyEnum.JOB_ID);
  46 + if (NumberUtil.isNumber(jobIdStr)) {
  47 + log.error("[executeInternal][Job({}) 获取不到正确的 jobId({})]", executionContext.getJobDetail().getKey(), jobIdStr);
  48 + throw new IllegalStateException(StrUtil.format("Job({}) 获取不到正确的 jobId({})",
  49 + executionContext.getJobDetail().getKey(), jobIdStr));
  50 + }
  51 + Long jobId = Long.valueOf(jobIdStr);
  52 + // 2. 获得 jobHandlerName 参数
27 53 String jobHandlerName = getJobData(executionContext, JobDataKeyEnum.JOB_HANDLER_NAME);
  54 + if (StrUtil.isEmpty(jobHandlerName)) {
  55 + log.error("[executeInternal][Job({}) 获取不到正确的 jobHandlerName({})]", executionContext.getJobDetail().getKey(), jobHandlerName);
  56 + throw new IllegalStateException(StrUtil.format("Job({}) 获取不到正确的 jobHandlerName({})",
  57 + executionContext.getJobDetail().getKey(), jobHandlerName));
  58 + }
  59 + // 3. 获得 jobHandlerParam 参数
  60 + String jobHandlerParam = getJobData(executionContext, JobDataKeyEnum.JOB_HANDLER_PARAM);
  61 +
  62 + Long jobLogId = null;
  63 + Date startTime = new Date();
  64 + try {
  65 + // 记录 Job 日志(初始)
  66 + jobLogId = jobLogFrameworkService.createJobLog(jobId, jobHandlerName, jobHandlerParam);
  67 + // 执行任务
  68 + String data = this.executeInternal(jobId, jobHandlerName, jobHandlerParam);
  69 + // 标记 Job 日志(成功)
  70 + Date endTime = new Date();
  71 + jobLogFrameworkService.updateJobLogSuccessAsync(jobLogId, endTime, diff(endTime, startTime), data);
  72 + } catch (ServiceException serviceException) {
  73 + // 标记 Job 日志(异常)
  74 + Date endTime = new Date();
  75 + jobLogFrameworkService.updateJobLogErrorAsync(jobLogId, endTime, diff(endTime, startTime),
  76 + serviceException.getCode(), serviceException.getMessage());
  77 + // 最终还是抛出异常,用于停止任务
  78 + throw serviceException;
  79 + } catch (Throwable e) {
  80 + // 标记 Job 日志(异常)
  81 + Date endTime = new Date();
  82 + jobLogFrameworkService.updateJobLogErrorAsync(jobLogId, endTime, diff(endTime, startTime),
  83 + INTERNAL_SERVER_ERROR.getCode(), ExceptionUtil.getRootCauseMessage(e));
  84 + // 最终还是抛出异常,用于停止任务
  85 + throw new JobExecutionException(e);
  86 + }
  87 + }
  88 +
  89 + private String executeInternal(Long jobId, String jobHandlerName, String jobHandlerParam) throws Exception {
  90 + // 获得 JobHandler 对象
28 91 JobHandler jobHandler = applicationContext.getBean(jobHandlerName, JobHandler.class);
  92 + Assert.isNull(jobHandler, "JobHandler 不会为空");
29 93  
30 94 // 执行任务
31   - String jobParam = getJobData(executionContext, JobDataKeyEnum.JOB_HANDLER_PARAM);
32   - try {
33   - jobHandler.execute(jobParam);
34   - } catch (Exception e) {
35   - // TODO 需要后续处理
  95 + CommonResult<String> result = jobHandler.execute(jobHandlerParam);
  96 + // 如果执行失败,则抛出 ServiceException 异常,方便统一记录
  97 + if (result.isError()) {
  98 + throw new ServiceException(result.getCode(), result.getMsg());
36 99 }
  100 + return result.getData();
37 101 }
38 102  
39 103 private static String getJobData(JobExecutionContext executionContext, JobDataKeyEnum key) {
... ...
src/main/java/cn/iocoder/dashboard/framework/quartz/core/service/JobLogFrameworkService.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.quartz.core.service;
  2 +
  3 +import java.util.Date;
  4 +
  5 +/**
  6 + * Job 日志 Framework Service 接口
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +public interface JobLogFrameworkService {
  11 +
  12 + /**
  13 + * 创建 Job 日志
  14 + *
  15 + * @param jobId 任务编号
  16 + * @param jobHandlerName Job 处理器的名字
  17 + * @param jobHandlerParam Job 处理器的参数
  18 + * @return Job 日志的编号
  19 + */
  20 + Long createJobLog(Long jobId, String jobHandlerName, String jobHandlerParam);
  21 +
  22 + /**
  23 + * 更新 Job 日志成功
  24 + *
  25 + * @param id 日志编号
  26 + * @param endTime 结束时间。因为是异步,避免记录时间不准去
  27 + * @param duration 运行时长,单位:毫秒
  28 + * @param data 成功数据
  29 + */
  30 + void updateJobLogSuccessAsync(Long id, Date endTime, Long duration, String data);
  31 +
  32 + /**
  33 + * 更新 Job 日志失败
  34 + *
  35 + * @param id 日志编号
  36 + * @param endTime 结束时间。因为是异步,避免记录时间不准去
  37 + * @param duration 运行时长,单位:毫秒
  38 + * @param code 错误码
  39 + * @param msg 异常提示
  40 + */
  41 + void updateJobLogErrorAsync(Long id, Date endTime, Long duration, Integer code, String msg);
  42 +
  43 +}
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/job/InfJobLogDO.java
... ... @@ -31,18 +31,6 @@ public class InfJobLogDO extends BaseDO {
31 31 */
32 32 private Long jobId;
33 33 /**
34   - * 任务名称
35   - *
36   - * 冗余字段 {@link InfJobDO#getName()}
37   - */
38   - private String jobName;
39   - /**
40   - * 任务分组
41   - *
42   - * 冗余字段 {@link InfJobDO#getGroup()}
43   - */
44   - private String jobGroup;
45   - /**
46 34 * 处理器的名字
47 35 *
48 36 * 冗余字段 {@link InfJobDO#getHandlerName()}
... ... @@ -74,16 +62,11 @@ public class InfJobLogDO extends BaseDO {
74 62 */
75 63 private Integer resultCode;
76 64 /**
77   - * 结果提示
78   - *
79   - * 目前使用的 {@link CommonResult#getMsg()} 属性
80   - */
81   - private String resultMsg;
82   - /**
83   - * 结果数据
  65 + * 结果
84 66 *
85   - * 目前使用的 {@link CommonResult#getData()} 数据
  67 + * 成功时,使用 {@link CommonResult#getData()} 数据
  68 + * 失败时,使用 {@link CommonResult#getMsg()} 属性
86 69 */
87   - private String resultData;
  70 + private String result;
88 71  
89 72 }
... ...
src/main/java/cn/iocoder/dashboard/util/date/DateUtils.java
... ... @@ -18,4 +18,8 @@ public class DateUtils {
18 18 return System.currentTimeMillis() > time.getTime();
19 19 }
20 20  
  21 + public static Long diff(Date endTime, Date startTime) {
  22 + return endTime.getTime() - startTime.getTime();
  23 + }
  24 +
21 25 }
... ...