先声明下,下面的讨论纯属技术讨论,由于操作代价比较大,实际意义并不大。
我们的开发一般分为开发环境,测试环境和生产环境。由于机器资源限制,开发环境和测试环境虽然部署着不同的应用,但是数据库都是同一个库。一般情况下,是可以这样玩的,也不会有什么问题。但是定时任务经常会出现开发环境启动的任务跑在了测试环境上,然后日志在开发环境里就查不到了。出现这种情况是因为在quartz.properties文件中,我们开启了集群模式:
org.quartz.jobStore.isClustered: true
这样一来,定时任务触发时,基本会随机选择一个节点运行这个任务。如何避免这样的情况呢?
一种方案是,不同的环境建不同的表,然后不同的环境使用不同的quartz.properties,更改表前缀:
org.quartz.jobStore.tablePrefix: MY_QRTZ_
这种方案比较简单,有个缺点就是要新建单独的一套表,本质上我们可以理解为还是使用了不同的库。
第二种方案是,不同的环境,使用不同的scheduler实例,并通过shcedulerName区分。Quartz中的所有表ched_name都是联合主键之一,不同的scheduler实例是不会相互影响的,这样属于开发环境的实例的定时任务就不会跑到测试环境中去。下面我们就来演示如何改造。
1、修改quartz.properties文件
将quart.properties文件拷贝一份,命名为quartz.dev.properties,然后修改instanceName
org.quartz.scheduler.instanceName: quartzSchedulerDev
2、修改quartz配置
针对不同的环境设置读取相应的quartz.properties文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- quartz scheduler配置 -->
<beans profile="dve">
<bean name="quartzScheduler" class="com.gameloft9.demo.jobs.MySchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jobFactory" ref="springJobFactory"/>
<property name="configLocation" value="classpath:quartz.dev.properties" />
<property name="overwriteExistingJobs" value="true" />
<property name="autoStartup" value="false" />
<property name="exposeSchedulerInRepository" value="true" />
</bean>
</beans>
<beans profile="!dev">
<bean name="quartzScheduler" class="com.gameloft9.demo.jobs.MySchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jobFactory" ref="springJobFactory"/>
<property name="configLocation" value="classpath:quartz.properties" />
<property name="overwriteExistingJobs" value="true" />
<property name="autoStartup" value="false" />
<property name="exposeSchedulerInRepository" value="true" />
</bean>
</beans>
</beans>
3、改造SchedulerFactoryBean
我们对SchedulerFactoryBean也需要做一点简单的改造。因为在SchedulerFactoryBean初始化Scheduler的时候,强行将上面配置的beanName覆盖了quartz.properties中的instanceName的值:
@Override
public void setBeanName(String name) {
if (this.schedulerName == null) {
this.schedulerName = name;
}
}
private void initSchedulerFactory(SchedulerFactory schedulerFactory) throws SchedulerException, IOException {
......
// Make sure to set the scheduler name as configured in the Spring configuration.
if (this.schedulerName != null) {
mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);
}
......
}
因此我们可以单独派生出一个类,然后将setBean重写,不要设置schdulerName即可。
package com.gameloft9.demo.jobs;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* 自定义SchedulerFactoryBean
* Created by gameloft9 on 2019/4/12.
*/
public class MySchedulerFactoryBean extends SchedulerFactoryBean {
@Override
public void setBeanName(String name) {
// 避免配置的beanName覆盖了quartz.properties中的org.quartz.scheduler.instanceName
return;
}
}
这样创建的SchedulerInstance的名称就是我们quartz.properties中配置的了。到此改造就结束了。
数据库截图
注意
不管是方案一,还是方案二,由于我们只是定时任务从环境上区分了,业务逻辑还是使用的同一个底层数据库。因此定时任务的执行间隔需要巧妙的设置下,以保持和原来单个环境下的执行频率一致。例如某个定时任务原本是每条凌晨1点执行,那么在开发环境和测试环境下,我们的时间就要设置成1号开发环境执行,2号测试环境执行。意思就是避免到点后,开发环境和测试环境同时执行了。通过这样的设置,定时任务就还是每天执行一次。