写的不好的地方请大家多多指正
上一篇讲解的环境的搭建,接着我们继续来讲集群,quartz的集群配置是在quartz.properties文件中,我们来修改一下quartz.properties 文件:
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
# 调度器实例的名字,使用默认的DefaultQuartzScheduler就好
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
# 调度器实例的ID, 选择AUTO
org.quartz.scheduler.instanceId:AUTO
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
# 跳过更新检查
org.quartz.scheduler.skipUpdateCheck:true
# 配置线程池
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
#
# quartz默认的是将job信息存储在内存中,quartz集群必须将job信息持久化到数据库中
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.misfireThreshold:60000
############
org.quartz.jobStore.useProperties:true
#quartz数据表的前缀,quartz的数据表在 quartz-2.2.3\docs\dbTables 文件夹中,
#选择对应的数据库版本,将数据库创建出来
org.quartz.jobStore.tablePrefix:QRTZ_
# 最关键的 是否支持集群 选择true
org.quartz.jobStore.isClustered:true
org.quartz.jobStore.clusterCheckinInterval:15000
# dataSource
#org.quartz.jobStore.dataSource:myDS
#org.quartz.dataSource.myDS.connectionProvider.class:com.abc.util.MyPoolingconnectionProvider
#org.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driver
#org.quartz.dataSource.myDS.url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8
#org.quartz.dataSource.myDS.user: root
#org.quartz.dataSource.myDS.password: root
#org.quartz.dataSource.myDS.maxConnections: 10
quartz.properties 配置完成,注意事项就是,需要创建数据库(推荐大家还是去官网下载quartz的包)。
这个配置文件中我将数据库连接的配置注释掉了,因为如果要在这里配置数据库连接的话,需要一个数据库连接的类,quartz本身的数据库连接类是有bug的,需要我们自己写一个数据库连接池的类 。就是这行配置:
org.quartz.dataSource.myDS.connectionProvider.class:com.abc.util.MyPoolingconnectionProvider
MyPoolingconnectionProvider.java 是安利的别人的,会在文章末尾贴出。
所以我不建议在quartz的配置文件中配置数据库连接,最好将数据库的连接配置放到Spring的配置文件中:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass">
<value>com.mysql.jdbc.Drivervalue>
property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost:3306/quartzvalue>
property>
<property name="user">
<value>rootvalue>
property>
<property name="password">
<value>rootvalue>
property>
<property name="maxPoolSize">
<value>20value>
property>
<property name="minPoolSize">
<value>2value>
property>
<property name="initialPoolSize">
<value>2value>
property>
<property name="maxIdleTime">
<value>20value>
property>
bean>
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:/quartz.properties" />
<property name="dataSource" ref="dataSource" />
<property name="overwriteExistingJobs" value="true" />
<property name="jobFactory">
<bean class="com.abc.util.JobFacotry" />
property>
<property name="startupDelay" value="1"/>
bean>
上面还有一个需要注意的地方就是,SchedulerFactoryBean 配置configLocation后,会覆盖quartz本身的配置文件,然后使用我们自定义的配置文件(就是我们修改好的那份)。
配置jobFactory的作用主要是为了在job任务类中可以注入其他的属性,如果不配置的话,job任务类中的属性将无法注入成功。JobFacotry.java也会在文章末尾贴出。
到了这里,quartz的集群配置已经完成,下一步就可以自己写job类,然后来调用了。
1.自定义Job类
自定义三个JobTest类,需要实现Quartz的Job接口。
2.创建Quartz 工具类
Quartz工具类的作用就是用来将 任务和 触发器 添加到 调度器中,从而执行调度工作;
public class QuartzUtil {
// 将配置文件中定义的SchedulerFactoryBean 注入到工具类中
@Autowired
@Qualifier("scheduler")
private Scheduler scheduler;
public void addJob(String name, String group, Class extends Job> clazz,String cronExpression) {
try {
//构造任务
JobDetail job = newJob(clazz)
.withIdentity(name, group)
.build();
//构造任务触发器
Trigger trg = newTrigger()
.withIdentity(name, group)
.withSchedule(cronSchedule(cronExpression))
.build();
//将作业添加到调度器
scheduler.scheduleJob(job, trg);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
该工具类,这里我只写了最简单的添加,关于移除,修改等更多的操作,请看这里
关于Cron表达式,请移步
3.工具类的使用
QuartzUtil创建完成之后,就可以通过SpringMVC进行调用了。
创建Controller:
@Controller
@RequestMapping("show")
public class TestController {
@Autowired
@Qualifier("quartz")
private QuartzUtil quartz;
@RequestMapping(value="quartz")
public String quartz(){
return "quartz";
}
@RequestMapping(value="start")
public void start(){
quartz.start();
}
@RequestMapping(value="newA")
public void newA(){
quartz.schedJob("jobA", null, com.abc.job.JobTest1.class, "*/3 * * * * ?");
}
@RequestMapping(value="modifyA")
public void modifyA(){
quartz.modifyTime("jobA", null, "*/5 * * * * ?");
}
@RequestMapping(value="removeA")
public void removeA(){
quartz.removeJob("jobA", null);
}
}
篇幅的原因,controller 做了省略,其他的直接调用QuartzUtil里面的方法即可。
全部完成之后,就可以将项目放在Tomcat上,运行成功后,便可以在Console中看到结果。
当然这个只是单个Tomcat节点,我们可以将项目打包,放在多个Tomcat下运行,Quartz自己实现了负载均衡和容错,当你的某一个tomcat挂掉之后,任务会在别的tomcat节点运行。接下来的内容大家可以自行测试!
小结:
Quartz集群配置注意的事项就是
1. 首先将Job信息实例化至数据库,所以需要Quartz的Sql文件来创建数据库(sql文件在quartz的包中 quartz-x.x.x \docs\dbTables 文件夹)。
2. 配置quartz.properties文件(可以在这里配置数据库连接,也可以在Spring的配置文件中配置)
3. 写QuartzUtil 类,实现任务调度。
//JobFacotry.java
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
public class JobFacotry extends AdaptableJobFactory{
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle)
throws Exception {
Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
//MyPoolingconnectionProvider.java
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
*
* @author wz
*
*/
public class MyPoolingconnectionProvider implements ConnectionProvider {
/** Default maximum number of database connections in the pool. */
public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
/** Default maximum number of database connections in the pool. */
public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
private String driver;
private String url;
private String user;
private String password;
private int maxConnections;
private int maxCachedStatementsPerConnection;
private int maxIdleSeconds;
private String validationQuery;
private int idleConnectionValidationSeconds;
private boolean validateOnCheckout;
private String discardIdleConnectionsSeconds;
private ComboPooledDataSource datasource;
/**
* 无参构造,必须要有[没有其他构造的话也可以不写]
*/
public MyPoolingconnectionProvider() {
}
public Connection getConnection() throws SQLException {
return datasource.getConnection();
}
public void shutdown() throws SQLException {
datasource.close();
}
/**
* 初始化方法,应该在调用其setter后调用
*/
public void initialize() throws SQLException {
if (this.url == null) {
throw new SQLException("DBPool could not be created: DB URL cannot be null");
}
if (this.driver == null) {
throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");
}
if (this.maxConnections < 0) {
throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");
}
datasource = new ComboPooledDataSource();
try {
datasource.setDriverClass(this.driver);
} catch (PropertyVetoException e) {
try {
throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);
} catch (SchedulerException e1) {
}
}
datasource.setJdbcUrl(this.url);
datasource.setUser(this.user);
datasource.setPassword(this.password);
datasource.setMaxPoolSize(this.maxConnections);
datasource.setMinPoolSize(1);
datasource.setMaxIdleTime(maxIdleSeconds);
datasource.setMaxStatementsPerConnection(this.maxCachedStatementsPerConnection);
if (this.validationQuery != null) {
datasource.setPreferredTestQuery(this.validationQuery);
if (!validateOnCheckout)
datasource.setTestConnectionOnCheckin(true);
else
datasource.setTestConnectionOnCheckout(true);
datasource.setIdleConnectionTestPeriod(this.idleConnectionValidationSeconds);
}
}
/*-------------------------------------------------
*
* setters 如果有必要,你可以添加一些getter
* ------------------------------------------------
*/
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUser(String user) {
this.user = user;
}
public void setPassword(String password) {
this.password = password;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public void setMaxCachedStatementsPerConnection(int maxCachedStatementsPerConnection) {
this.maxCachedStatementsPerConnection = maxCachedStatementsPerConnection;
}
public void setMaxIdleSeconds(int maxIdleSeconds) {
this.maxIdleSeconds = maxIdleSeconds;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
}
public void setValidateOnCheckout(boolean validateOnCheckout) {
this.validateOnCheckout = validateOnCheckout;
}
public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {
this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;
}
public void setDatasource(ComboPooledDataSource datasource) {
this.datasource = datasource;
}
protected ComboPooledDataSource getDataSource() {
return datasource;
}
}