关于Spring boot中的Quartz配置启动开关问题(启动、停止)

最近在做一个项目,项目中有多个模块,总体构架为SpringBoot +Quartz+其他(其他的不是重点啦)。

问题提出:

项目中会有Quartz做的定时任务模块,至于怎么使用Quartz网上一搜一大把,在这里不再细讲,

我要说的是,在开发和实际部署环境中如何避免定时任务重复执行?

例如:项目开发完成后已经部署到服务器,但是后期肯定是需要维护的呀,

那么问题来了,你在本地开发环境启动项目的时候,如果不做处理,呵呵。。。

结果可想而知,同一个定时任务,必然会执行两遍,如果是N个人维护这个程序,任务必将执行N+1次(服务器 一次)。

当然,前提是你们用的同一个数据库,大多数情况下为了偷懒(其实也是为了数据一致性啦),

程序猿一般会使用真实的服务器来运行程序。

 

那么如何在修改其他(非定时任务)模块时,在开发环境中关闭Quartz的定时任务呢?当然还不能影响服务器的任务执行。

在网上漂了大半天,也没有实际有效的解决办法,下面说说我的解决办法吧,如果你有更好的办法,也很希望你能告诉我,让我们共同进步。

首先,不同的环境我是有不同的配置文件的,主配置文件内容如下:

spring:
  profiles:
    #    生产环境
    # active: prd
    #    开发环境
    active: dev

备注:我用的YML文件格式,其他格式类似,遵循其道即可。

在实际配置文件中加入开关(自定义的名字,可以改为你喜欢的名字)选项:

开发环境:

    # 开启定时任务 true为启动 false为关闭
    scheduling:
        enabled: false

 生产环境:

    # 开启定时任务 true为启动 false为关闭
    scheduling:
        enabled: true

 配置文件到此为止,简单吧,以下代码是QuartzSchedule中控制Schedule的配置和执行。

------------ 分割线 --------------------------------------------------

以下内容可能根据不同的人来说并不相同,请不要说你找不到这些代码,

你找一下你的Schedule是在哪里控制的,加入开关控制即可。

 

我在网上搜了好久也没有好的控制办法,经过对Quartz代码的解读,我发现Quartz的初始化是SpringBoot在内部自动加载的,

很难去不让它加载,不然你要修改Quartz的内部代码,不是不可以,但这必然不是我们想要的。

 

于是我想了个曲线救国的方式:Schedule可以初始化和加载,去控制它的执行,这个Schedule是程序运行时初始化的,

必然是单独运行的,所以修改它不会影响服务器的定时任务。

SchedulerFactoryBean factory = new SchedulerFactoryBean()

这个是我程序中的调度工厂类,你的可能会不同,但大同小异,这个会生成具体的Schedule实体,此处初始化时会设置Schedule的相关属性:

@Configuration
public class ScheduleConfig
{
    //Jerry add for schedule control at 2020-2-22
    @Value("${jmbj.scheduling.enabled}")
    private Boolean scheduleEnableFlg;
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
    {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);

        // quartz参数
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "JmbjScheduler");
        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.txIsolationLevelSerializable", "true");

        // sqlserver 启用
        // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        factory.setQuartzProperties(prop);

        factory.setSchedulerName("JmbjScheduler");
        // 延时启动
        factory.setStartupDelay(1);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        // 可选,QuartzScheduler
        // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        factory.setOverwriteExistingJobs(true);

        //Jerry edit at 2020-2-22
        if (!scheduleEnableFlg) {
            factory.setAutoStartup(false);
        }else
        {
            // 设置自动启动,默认为true
            factory.setAutoStartup(true);
        }
        return factory;
    }
}

上面的属性中,如果要关闭schedule必须将自动启动的选项设置为false,其他属性按你的需要进行设置即可。

 

以上配置加载后会去实例化schedule对象,如下图所示:

 @Autowired
    private Scheduler scheduler;

    @Autowired
    private SysJobMapper jobMapper;

    //Jerry add for schedule control at 2020-2-22
    @Value("${jmbj.scheduling.enabled}")
    private Boolean scheduleEnableFlg;
    /**
     * 项目启动时,初始化定时器 
     * 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException {

        //Jerry edit at 2020-2-22
        if (scheduleEnableFlg) {
            List jobList = jobMapper.selectJobAll();
            for (SysJob job : jobList) {
                updateSchedulerJob(job, job.getJobGroup());
            }
            log.info("定时任务已启动。");
        } else {
//            scheduler.shutdown();
            log.info("定时任务未启动。");
        }
    }

  这个是从配置文件中读取控制开关。

@Value("${jmbj.scheduling.enabled}")
    private Boolean scheduleEnableFlg;

以上是初始化Schedule后会去数据库中查找设置的定时任务:

如果是开发环境必然也是没必要去查询数据库了,这样必然也不会去执行定时任务啦!

 

需要注意的地方:开始还将schedule设置了shutdown();可以设置,但是没有必要,因为schedule中没有任何任务,所以不关闭也无所谓,性能嘛,我觉得可以忽略,但未测试。切记设置自动启动未false!!

转载请注明出处,如有其它办法,欢迎一起讨论。

 

你可能感兴趣的:(Java)