Quartz入门探究

来源:互联网 发布:阿里云消息推送 编辑:程序博客网 时间:2024/05/21 17:04
1. 什么是Quartz

Quartz用通俗的话说就是一种任务定时调度框架,支持多种各种复杂的任务调度。Quartz是OpenSymphony开源组织在Jobscheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统

2. Quartz作业存储方式

第一种类型叫做RAMJobStore,它利用通常的内存来持久化调度程序信息。这种作业存储类型最容易配置、构造和运行。对许多应用来说,这种作业存储已经足够了。 然而,因为调度程序信息是存储在被分配给 JVM的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。如果你需要在重新启动之间持久化调度信息.
第二种类型的作业存储实际上提供两种不同的实现,但两种实现一般都称为JDBC作业存储两种JDBC作业存储都需要JDBC驱动程序和后台数据库来持久化调度程序信息。

3. Quartz基于数据库存储方式的实现

csdn博客中很多人写了数据存储方式实现的job存储方式,但是绝大部分demo都是无法实现数据库存储job,通过对其它博主的demo实现的运行,对报错的代码进行debug与源码跟踪,我实现了一种可行的纯jdbc存储job的方式,实现如下:
首先实现一个SimpleJob类,这个类实现的是quartz中的job接口,并实现execute方法,这个方法中实现定义的job所需要实现的逻辑,简单代码如下:

package quartz;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import java.text.SimpleDateFormat;import java.util.Date;public class SimpleJob implements Job {    public void execute(JobExecutionContext context) throws JobExecutionException {        System.out.println("Hello quzrtz  "+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()));    }}

我们配置一下quartz.properties文件,注意看我们配置中实现 了一个自定义是数据库连接池:

# Default Properties file for use by StdSchedulerFactory# to create a Quartz Scheduler Instance, if a different# properties file is not explicitly specified.##集群配置org.quartz.scheduler.instanceName: DefaultQuartzSchedulerorg.quartz.scheduler.rmi.export: falseorg.quartz.scheduler.rmi.proxy: falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction: falseorg.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount: 10org.quartz.threadPool.threadPriority: 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60#============================================================================# Configure JobStore#============================================================================#默认配置,数据保存到内存#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore#持久化配置org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegateorg.quartz.jobStore.clusterCheckinInterval = 20000org.quartz.jobStore.useProperties:false#数据库表前缀org.quartz.jobStore.tablePrefix:qrtz_org.quartz.jobStore.dataSource:myDS#============================================================================# Configure Datasources#============================================================================#JDBC驱动org.quartz.dataSource.myDS.connectionProvider.class:quartz.PoolingconnectionProviderorg.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driverorg.quartz.dataSource.myDS.url:jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8org.quartz.dataSource.myDS.user:rootorg.quartz.dataSource.myDS.password:symanorg.quartz.dataSource.myDS.maxConnections:10

本实验最初的quartz.properties文件如下

# Default Properties file for use by StdSchedulerFactory# to create a Quartz Scheduler Instance, if a different# properties file is not explicitly specified.##集群配置org.quartz.scheduler.instanceName: DefaultQuartzSchedulerorg.quartz.scheduler.rmi.export: falseorg.quartz.scheduler.rmi.proxy: falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction: falseorg.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount: 10org.quartz.threadPool.threadPriority: 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60#============================================================================# Configure JobStore#============================================================================#默认配置,数据保存到内存#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore#持久化配置org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegateorg.quartz.jobStore.clusterCheckinInterval = 20000org.quartz.jobStore.useProperties:false#数据库表前缀org.quartz.jobStore.tablePrefix:qrtz_org.quartz.jobStore.dataSource:myDS#============================================================================# Configure Datasources#============================================================================#JDBC驱动org.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driverorg.quartz.dataSource.myDS.url:jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8org.quartz.dataSource.myDS.user:rootorg.quartz.dataSource.myDS.password:symanorg.quartz.dataSource.myDS.maxConnections:10

为何要这样修改,就是在实验过程中,quartz原生自带的c3p0连接池无法获取数据库连接信息,创建数据库连接。这是通过调试源码实现。会报一个如下错误如下图1:
这里写图片描述

图1

自定义实现的数据连接池如下:

package quartz;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.quartz.SchedulerException;import org.quartz.utils.ConnectionProvider;import java.beans.PropertyVetoException;import java.sql.Connection;import java.sql.SQLException;/** * @author  * @time 2017/10/12 * @desc 自定义数据库连接池如下 */public class PoolingconnectionProvider implements ConnectionProvider {    public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;    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 PoolingconnectionProvider() {    }    public Connection getConnection() throws SQLException {        return datasource.getConnection();    }    public void shutdown() throws SQLException {        datasource.close();    }    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);        }    }    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;    }    public static int getDefaultDbMaxConnections() {        return DEFAULT_DB_MAX_CONNECTIONS;    }    public static int getDefaultDbMaxCachedStatementsPerConnection() {        return DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION;    }    public String getDriver() {        return driver;    }    public String getUrl() {        return url;    }    public String getUser() {        return user;    }    public String getPassword() {        return password;    }    public int getMaxConnections() {        return maxConnections;    }    public int getMaxCachedStatementsPerConnection() {        return maxCachedStatementsPerConnection;    }    public int getMaxIdleSeconds() {        return maxIdleSeconds;    }    public String getValidationQuery() {        return validationQuery;    }    public int getIdleConnectionValidationSeconds() {        return idleConnectionValidationSeconds;    }    public boolean isValidateOnCheckout() {        return validateOnCheckout;    }    public String getDiscardIdleConnectionsSeconds() {        return discardIdleConnectionsSeconds;    }    public ComboPooledDataSource getDatasource() {        return datasource;    }}

数据库连接与配置文件与自定义job完成之后,接下来进行quartz调度的测试,本实例中,写了两个函数,startSchedule()创建一个quartz任务,任务的任务名称,任务组,触发器名字写入数据库,resumeJob() 恢复数据库中某个任务组下的触发器的任务,代码如下:

package quartz;import org.quartz.*;import org.quartz.impl.StdSchedulerFactory;import java.text.ParseException;import java.util.List;/** * @author  * @time 2017/9/26 * @desc 本实例中,写了两个函数,startSchedule()创建一个quartz任务,任务的任务名称,任务组,触发器名字写入数据库 *                                resumeJob() 恢复数据库中某个任务组下的触发器的任务     */public class QuartzTest {    public static void main(String[] args) throws SchedulerException,            ParseException {        startSchedule();//        resumeJob();    }    /**     * @time 2017/9/26     * @desc   创建quartz任务步骤如下 :     *          1、创建一个JobDetail实例,指定Quartz 任务执行类 任务名,任务组     *          2、创建Trigger     *          3、创建Scheduler     *          4、调度执行     */    public static void startSchedule() {        try {            JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class).withIdentity("job1_4", "jGroup1").build();            SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForTotalCount(100);// 设置执行次数            Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1_3", "jGroup1").startNow().withSchedule(builder).build();            SchedulerFactory schedulerFactory = new StdSchedulerFactory("quartz.properties");            Scheduler scheduler = schedulerFactory.getScheduler();            scheduler.start();            scheduler.scheduleJob(jobDetail, trigger);            try {                Thread.sleep(60000);            } catch (InterruptedException e) {                e.printStackTrace();            }            scheduler.shutdown();        } catch (SchedulerException e) {            e.printStackTrace();        }    }    /**     * @time 2017/9/26     * @desc   恢复quartz任务步骤如下 :     *          1、获取调度器中所有的触发器组     *          2、重新恢复在任务组tgroup1中,触发器名为trigger1_1的任务     *          3、恢复运行     */    public static void resumeJob() {        try {            SchedulerFactory schedulerFactory = new StdSchedulerFactory();            Scheduler scheduler = schedulerFactory.getScheduler();            List<String> triggerGroups = scheduler.getTriggerGroupNames();            for (int i = 0; i < triggerGroups.size(); i++) {                List<String> triggers = scheduler.getTriggerGroupNames();                for (int j = 0; j < triggers.size(); j++) {                    Trigger tg = scheduler.getTrigger(new TriggerKey(triggers.get(j), triggerGroups.get(i)));                    if (tg instanceof SimpleTrigger                            && tg.getDescription().equals("tgroup1.trigger1_3")) {                        scheduler.resumeJob(new JobKey(triggers.get(j), triggerGroups.get(i)));                    }                }            }            scheduler.start();        } catch (Exception e) {            e.printStackTrace();        }    }}

执行一次,可以看到控制台输出如下:
这里写图片描述

图2

同时可以看到数据会存储jobdetail信息如下:
这里写图片描述
图3

数据库中会存储quartz.properties中的定义的调度器的名字DefaultQuartzScheduler。恢复一个任务的时候可以查看数据中存储了哪些任务组中有哪些触发器trigger_name的任务,并恢复他们。任务组与触发器名字存储在qrtz_fired_triggers表中。如下图4:
这里写图片描述

图4
原创粉丝点击