springcloud的可配置化定时任务的设计及实现

一、开启定时器,需要在启动类和定时任务类上添加注解
–>springboot+springcloud的定时任务

二、设计定时任务的数据库,通过数据库动态配置定时任务。

-- auto-generated definition
create table timer_config
(
    creation_date    datetime      not null,
    created_by       bigint        not null,
    last_update_date datetime(3)   null,
    last_updated_by  bigint        not null,
    enabled_flag     varchar(1)    not null,
    timer_config_id  bigint auto_increment comment '主键'
        primary key,
    timer_code       varchar(64)   not null comment '定时器编码',
    timer_name       varchar(1024) not null comment '定时器名称',
    timer_ip         varchar(64)   not null comment '定时器可执行IP',
    timer_cron       varchar(32)   not null comment '定时器执行的表达式(0 10 6 * * ?)(* 0/10 * * * ?)',
    timer_para       varchar(512)  null comment '定时器带入参数(JSON字符)',
    timer_app        varchar(32)   null comment '定时器应用模块(应用名)',
    log_flag         varchar(1)    not null comment '是否记录运行日志(打印在日志控制台)',
    constraint timer_config_u1
        unique (timer_code)
)
    comment '定时器配置';

creation_date --> 创建时间
created_by --> 创建人id
last_update_date–> 最后更新时间
last_updated_by --> 最后更新人id
enabled_flag --> 是否有效

三、基于SchedulingConfigurer接口实现配置化的定时任务
@Schedule 注解的方式,缺点是不能动态配置,SchedulingConfigurer可以做到。


/**
 * 如果大时间段改为小时间段或者更改IP,需要重启应用才能立即生效。比如:1天改为1小时
 */
public abstract class AbstractTimer implements SchedulingConfigurer {
    protected final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    protected TimerConfigService timerConfigService;

    @Autowired
    protected MonitorSendByMqService monitorSendByMqService;

    @Autowired
    private CacheService cache;

    private TimerConfig getTimerConfig(ContextInfo context, String timerCode) {
        String key = CacheKeyUtil.buildKey(TimerConfig.class, timerCode);
        try {
            return cache.get(key, () -> {
                TimerConfig config = timerConfigService.getTimerConfig(timerCode);
                if (null == config) {
                    log.error("找不到定时器配置。timerCode={},sessionId={}", timerCode, context.getSessionId());
                }
                return config;
            });
        } catch (Exception e) {
            log.error("查询缓存的定时器配置出错。timerCode={},sessionId={}", timerCode, context.getSessionId(), e);
            return null;
        }
    }

    @SuppressWarnings("rawtypes")
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ContextInfo context = ContextInfo.buildTimer();//构建上下文
        Set<String> netIps = BeeGetLocalIpUtil.get();//获取ip地址 https://blog.csdn.net/shihuahao0353/article/details/121167878
        String timerCode = this.getTimerCode();
        TimerConfig config = this.getTimerConfig(context, timerCode);
        if (null == config) {
            return;
        }
        taskRegistrar.addTriggerTask(() -> {
            if (YesOrNo.Y.val.equals(config.getLogFlag())) {
                log.info("定时任务执行[开始], timerCode={},sessionId={}", timerCode, context.getSessionId());
            }
            // 判断定时器是否可以在本机运行
            if (!this.isRun(config.getTimerIp(), netIps)) {
                log.error("定时任务执行失败,IP不合法, timerCode={},执行ip={},合法ip={},sessionId={}", timerCode, netIps.toString(), config.getTimerIp(), context.getSessionId());
                return;
            }
            long start = System.currentTimeMillis();
            // 执行业务逻辑
            try {
                CommonResult cr = this.process(context, BeeStringUtil.isEmpty(config.getTimerPara()) ? null : JSONObject.parseObject(config.getTimerPara()));
                if (cr == null) {
                    log.error("定时任务执行失败,业务处理未返回处理结果, timerCode={},sessionId={}", timerCode, context.getSessionId());
                    return;
                } else if (!cr.isSuccess()) {
                    log.error("定时任务执行失败,业务处理未返回错误, timerCode={},msg={},sessionId={}", timerCode, cr.getMessage(), context.getSessionId());
                    return;
                }
            } catch (BusinessException e) {
                // 业务异常不发通知
                log.error("执行定时器任务出错!timerCode={},sessionId={}", timerCode, context.getSessionId(), e);
            } catch (Exception e) {
                log.error("执行定时器任务异常!timerCode={},sessionId={}", timerCode, context.getSessionId(), e);
                // 运行时异常发通知
                this.sendMonitorNotice("执行定时器任务异常!timerCode=" + timerCode + ",sessionId=" + context.getSessionId());
            } finally {
                
                if (YesOrNo.Y.val.equals(config.getLogFlag())) {
                    long end = System.currentTimeMillis();
                    log.info("定时任务执行[结束]。timerCode={},耗时={}毫秒,sessionId={}", timerCode, end - start, context.getSessionId());
                }
            }
        }, triggerContext -> {
//            log.info("====获取定时器配置,sessionId={},TimerCode={}", context.getSessionId(), timerCode);
            return new CronTrigger(config.getTimerCron()).nextExecutionTime(triggerContext);
        });
    }

    @SuppressWarnings("rawtypes")
    protected abstract CommonResult process(ContextInfo context, JSONObject para) throws Exception;

    /** timer_config表中的timer_code字段 */
    protected abstract String getTimerCode();

    private boolean isRun(String configIps, Set<String> netIps) {
        for (String configIp : configIps.split(",")) {
            for (String netIp : netIps) {
                if (configIp.equals(netIp)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void sendMonitorNotice(String msg) {
        try {
            SysMonitorBean bean = SysMonitorBean.buildMail("定时器运行异常", msg, null);
            monitorSendByMqService.send(bean);
        } catch (Exception e) {
            log.error("发送错误监控消息异常:" + e.getMessage(), e);
        }
    }
}

定时任务的实现类继承AbstractTimer抽象类,添加@Component注解,实现抽象方法,就可以实现通过数据库来灵活配置定时器了。

附:CommonResult类

public class CommonResult<T> implements java.io.Serializable {

    private static final long serialVersionUID = 2683217789586688528L;

    private int status;

    private String message;

    private T data;

    public CommonResult() {
    }

    public CommonResult(int status, String message, T data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }

    // 成功
    public static <T> CommonResult<T> success() {
        return new CommonResult<T>(RespStatus.SUCCESS, null, null);
    }

    public static <T> CommonResult<T> successWithMessage(String msg) {
        return new CommonResult<T>(RespStatus.SUCCESS, msg, null);
    }

    public static <T> CommonResult<T> successWithMessage(String msg, Object... objects) {
        return new CommonResult<T>(RespStatus.SUCCESS, String.format(msg, objects), null);
    }

    public static <T> CommonResult<T> successWithData(T data) {
        return new CommonResult<T>(RespStatus.SUCCESS, null, data);
    }

    // 失败
    public static <T> CommonResult<T> fail() {
        return new CommonResult<T>(RespStatus.BUSIENSSERROR, null, null);
    }

    public static <T> CommonResult<T> failWithMessageAndData(String msg, T data) {
        return new CommonResult<T>(RespStatus.BUSIENSSERROR, msg, data);
    }
    
    public static <T> CommonResult<T> failWithMessage(String msg) {
        return new CommonResult<T>(RespStatus.BUSIENSSERROR, msg, null);
    }

    public static <T> CommonResult<T> failWithMessage(String msg, Object... objects) {
        return new CommonResult<T>(RespStatus.BUSIENSSERROR, String.format(msg, objects), null);
    }
    
    public static <T> CommonResult<T> build(int status, String message, T data) {
        return new CommonResult<T>(status, message, data);
    }

    //
    public boolean isSuccess() {
        return RespStatus.SUCCESS == this.getStatus();
    }

    public boolean isFailure() {
        return !this.isSuccess();
    }

    //
    public String getMessage() {
        return message;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

你可能感兴趣的:(spring,cloud,java)