Quartz任务调度(一)

一、引入依赖



      org.springframework.boot
      spring-boot-starter-quartz



    com.alibaba
    druid-spring-boot-starter
    1.1.9



    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    1.3.2



    mysql
    mysql-connector-java
    5.1.47



    org.projectlombok
    lombok
    true



    log4j
    log4j
    1.2.17



    com.baomidou
    mybatis-plus-boot-starter
    3.0.6

二、配置文件application.properties

## database
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://192.168.0.246:3306/hrz_task_center?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=1111

# Druid数据源配置
spring.datasource.driverClassName=com.mysql.jdbc.Driver
# 连接池配置
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.exceptionSorter=true
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.filters=stat,wall,log4j
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
spring.datasource.useGlobalDataSourceStat=true
# Druid监控器配置
#默认为stat,即开启sql监控。这里加了个wall,表示同时开启sql防火墙
spring.datasource.druid.filters=stat,wall,log4j
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.web-stat-filter.session-stat-enable=true
spring.datasource.druid.web-stat-filter.session-stat-max-count=10
spring.datasource.druid.web-stat-filter.principal-session-name=
# Druid管理页配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
#监控页面登录用户名
spring.datasource.druid.stat-view-servlet.login-username=admin
#监控页面登录密码
spring.datasource.druid.stat-view-servlet.login-password=admin
# Druid AOP 配置
# Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔
spring.datasource.druid.aop-patterns=com.hrz.task.service.*
spring.aop.proxy-target-class=true

#连接数据库
#mybaties配置
#扫描实力类的所在包
mybatis-plus.type-aliases-package=com.hrz.task.entity
#通用mapper的所在接口名称 不只是包名
mybatis-plus.mapper-locations=classpath*:/mappers/**/*.xml
#在格式:logging.level.Mapper类的包=debug  会在控制台打印出sql语句
logging.level.com.hrz.third.mapper=debug
mybatis-plus.global-config.id-type=0
mybatis-plus.global-config.field-strategy=1
mybatis-plus.global-config.db-column-underline=true
mybatis-plus.global-config.refresh-mapper=true
mybatis-plus.global-config.db-config.table-underline=true
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.cache-enabled=false
mybatis-plus.configuration.call-setters-on-nulls=true
mybatis-plus.global-config.db-config.table-prefix=task_

三、配置类ScheduleConfig

@Configuration
public class ScheduleConfig {
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        //配置数据源
        factory.setDataSource(dataSource);
        //quartz参数
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "HrzScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        //线程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        //JobStore配置
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        //集群配置
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        //前缀
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");

        //PostgreSQL数据库,需要打开此注释
        //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
        factory.setQuartzProperties(prop);
        //调度名称
        factory.setSchedulerName("HrzScheduler");
        //延时启动
        factory.setStartupDelay(30);
        //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        factory.setOverwriteExistingJobs(true);
        //设置自动启动,默认为true
        factory.setAutoStartup(true);
        return factory;
    }
}

四、工具类

1、ScheduleJob
@Slf4j
public class ScheduleJob extends QuartzJobBean {
    private ExecutorService service = Executors.newSingleThreadExecutor();

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        JobKey key = context.getJobDetail().getKey();
        log.info( "任务key:"+key);
        String json = context.getMergedJobDataMap().getString(ScheduleJobs.JOB_PARAM_KEY);
        //将获取的对象序列化的json 转化为实体类对象
        ScheduleJobs scheduleJobs = JSON.parseObject(json, ScheduleJobs.class);
        //获取spring bean
        ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringUtils.getBean("scheduleJobLogService");

        //数据库保存执行记录
        ScheduleJobLog scheduleJobLog = new ScheduleJobLog();

        scheduleJobLog.setJobId(scheduleJobs.getId());
        scheduleJobLog.setBeanName(scheduleJobs.getBeanName());
        scheduleJobLog.setMethodName(scheduleJobs.getMethodName());
        scheduleJobLog.setParams(scheduleJobs.getParams());
        scheduleJobLog.setCreateTime(new Date());

        //任务开始时间
        long startTime = System.currentTimeMillis();

        try {
            //执行任务
            log.info("任务准备执行,任务ID:" + scheduleJobs.getId());
            ScheduleRunnable task = new ScheduleRunnable(scheduleJobs.getBeanName(),
                    scheduleJobs.getMethodName(), scheduleJobs.getParams());
            //接收多线程的执行结果
            Future future = service.submit(task);
            future.get();
            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            scheduleJobLog.setTimes((int)times);
            //任务状态    0:成功    1:失败
            scheduleJobLog.setStatus(0);
            log.info("任务执行完毕,任务ID:" + scheduleJobs.getId() + "  总共耗时:" + times + "毫秒");
        } catch (Exception e) {
            log.error("任务执行失败,任务ID:" + scheduleJobs.getId(), e);

            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            scheduleJobLog.setTimes((int)times);

            //任务状态    0:成功    1:失败
            scheduleJobLog.setStatus(1);
            scheduleJobLog.setError(StringUtils.substring(e.toString(), 0, 2000));
        }finally {
            //插入日志
            scheduleJobLogService.saveLog(scheduleJobLog);
        }
    }
}
2、ScheduleUtils
@Slf4j
public class ScheduleUtils {
    private final static String JOB_NAME = "TASK_";

    /**
     * 获取触发器key
     */
    public static TriggerKey getTriggerKey(String jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取jobKey
     */
    public static JobKey getJobKey(String jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 获取表达式触发器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) throws HrzException {
        try {
            log.info("获取表达式触发器:" + jobId + ":" + scheduler);
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            log.error("获取定时任务CronTrigger出现异常" + e);
            throw new HrzException("获取定时任务CronTrigger出现异常", 500);
        }
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, ScheduleJobs scheduleJobs) throws HrzException {
        try {
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJobs.getId())).build();

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJobs.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJobs.getId())).withSchedule(scheduleBuilder).build();

            //放入参数,运行时的方法可以获取
            //将对象josn序列化存储到Job的getJobDataMap()方法中,为后续根据获取属性执行对应的类的任务
            jobDetail.getJobDataMap().put(ScheduleJobs.JOB_PARAM_KEY, JSON.toJSONString(scheduleJobs));
            scheduler.scheduleJob(jobDetail, trigger);

            //暂停任务
            if (scheduleJobs.getStatus() == GlobalConstant.ScheduleStatus.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJobs.getId());
            }
        } catch (SchedulerException e) {
            throw new HrzException("创建定时任务失败" + e, 500);
        }
    }

    /**
     * 更新定时任务
     */
    public static void updateScheduleJob(Scheduler scheduler, ScheduleJobs scheduleJobs) throws HrzException {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJobs.getId());

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJobs.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJobs.getId());

            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            //将对象放入触发器中
            trigger.getJobDataMap().put(ScheduleJobs.JOB_PARAM_KEY, JSON.toJSONString(scheduleJobs));
            //接新的触发器重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);

            //暂停任务
            if (scheduleJobs.getStatus() == GlobalConstant.ScheduleStatus.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJobs.getId());
            }

        } catch (SchedulerException e) {
            throw new HrzException("更新定时任务失败", 500);
        }
    }

    /**
     * 立即执行任务
     */
    public static void run(Scheduler scheduler, ScheduleJobs scheduleJobs) throws HrzException {
        try {
            log.info("立即执行任务:" + scheduleJobs.getId());
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleJobs.JOB_PARAM_KEY, JSON.toJSONString(scheduleJobs));
            scheduler.triggerJob(getJobKey(scheduleJobs.getId()), dataMap);
        } catch (SchedulerException e) {
            throw new HrzException("立即执行定时任务失败" + e, 500);
        }
    }

    /**
     * 暂停任务
     */
    public static void pauseJob(Scheduler scheduler, String jobId) throws HrzException {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new HrzException("暂停定时任务失败", 500);
        }
    }

    /**
     * 恢复任务
     */
    public static void resumeJob(Scheduler scheduler, String jobId) throws HrzException {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new HrzException("暂停定时任务失败", 500);
        }
    }

    /**
     * 删除定时任务
     */
    public static void deleteScheduleJob(Scheduler scheduler, String jobId) throws HrzException {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new HrzException("删除定时任务失败", 500);
        }
    }
}
3、
Slf4j
public class ScheduleRunnable implements Runnable {
    private Object target;
    private Method method;
    private String params;

    public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
        this.target = SpringUtils.getBean(beanName);
        this.params = params;

        if(StringUtils.isNotBlank(params)){
            this.method = target.getClass().getDeclaredMethod(methodName, String.class);
        }else{
            this.method = target.getClass().getDeclaredMethod(methodName);
        }
    }

    @Override
    public void run() {
        try {
            ReflectionUtils.makeAccessible(method);
            if(StringUtils.isNotBlank(params)){
                method.invoke(target, params);
            }else{
                method.invoke(target);
            }
        }catch (Exception e) {
            log.error("执行定时任务失败", 500);
        }
    }
}
3、ScheduleRunnable
@Slf4j
public class ScheduleRunnable implements Runnable {
    private Object target;
    private Method method;
    private String params;

    public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
        this.target = SpringUtils.getBean(beanName);
        this.params = params;

        if(StringUtils.isNotBlank(params)){
            this.method = target.getClass().getDeclaredMethod(methodName, String.class);
        }else{
            this.method = target.getClass().getDeclaredMethod(methodName);
        }
    }

    @Override
    public void run() {
        try {
            ReflectionUtils.makeAccessible(method);
            if(StringUtils.isNotBlank(params)){
                method.invoke(target, params);
            }else{
                method.invoke(target);
            }
        }catch (Exception e) {
            log.error("执行定时任务失败", 500);
        }
    }
}

你可能感兴趣的:(Quartz任务调度(一))