转载

通过Quartz如何Debug , 如何修复Bug

通过Quartz如何Debug , 如何修复Bug

​ 我们以Quartz的用JDBC作为JobStore为例子

开始很简单绝对是 :

org.quartz.spi.JobStore 找他的接口实现类 -> 通过包名字可以发现 -> org.quartz.impl.jdbcjobstore 原来是JDBC , 继续 -> 有三个实现

通过Quartz如何Debug , 如何修复Bug

很简单 我们绝对要使用实现类哇, 因为抽象类是不可能靠反射生成的 .

这哦俩区别一个支持事务, 一个不支持事务 , TX transaction就是 . 我们选择事务的.

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
复制代码

继续, 然后我们这个配置好了, 咋启动哇, 它又不知道JDBC配置是啥. 因此我们看我们上面的实现类 , 然后我们没有点开 JobStore 找哪里使用过, 因为这个是在初始化的时候实现的, 我们就判断在 org.quartz.impl.StdSchedulerFactory 这里 , 进去发现, 他使用了

js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
复制代码

那么 jsClass是咋获取的 ? 原来如此...

String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,RAMJobStore.class.getName());
复制代码

我们继续呆着问题走 /.....

下面走着就看到一句

// Set up any DataSources
复制代码

继续看这里 , PROP_DATASOURCE_PREFIX=org.quartz.dataSource

String[] dsNames = cfg.getPropertyGroups(PROP_DATASOURCE_PREFIX);
复制代码

进去 ... 这个 org.quartz.utils.PropertiesParser#getPropertyGroups

public String[] getPropertyGroups(String prefix) {
    Enumeration<?> keys = props.propertyNames();
    HashSet<String> groups = new HashSet<String>(10);

    if (!prefix.endsWith(".")) {
        prefix += ".";
    }

    while (keys.hasMoreElements()) {
        String key = (String) keys.nextElement();
        if (key.startsWith(prefix)) {
            String groupName = key.substring(prefix.length(), key.indexOf(
                    '.', prefix.length()));
            groups.add(groupName);
        }
    }

    return (String[]) groups.toArray(new String[groups.size()]);
}
复制代码

我再这里绕了很久, 咋获取了, 其实很简单, 就是获取前缀 org.quartz.dataSource.quartz.driver的前缀就是quartz他就是获取这个. 然后set去重 , 就获取到 group了 . 其实这里处理也很好 , 第一对于properties文件来说, 他的list是一个arg[1]=? , 对于spring来说 , 我个人比较喜欢properties和xml配置文件.

其实这个工具类真心不赖, 处理很方便, 这里告诉大家一个技巧, 可以引用别人的代码 , 但是我觉得尊重原创, 就是加个作者名字, 留个链接也行 .

我们继续往下走 ... StdSchedulerFactory 925行

PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(PROP_DATASOURCE_PREFIX + "." + dsNames[i], true));
// 很简单就是获取一个去了前缀的properties
复制代码
//  connectionProvider.class -> 这里就比较坑,往下走
String cpClass = pp.getStringProperty(PROP_CONNECTION_PROVIDER_CLASS, null);

// 默认构造器实例化, 显然是不行的.  ConnectionProvider , 实现类没有这么的
 cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();
复制代码

我们就放弃了 , 此时到了 Jndi ,这个我们也不考虑, 继续

String poolingProvider = pp.getStringProperty(PoolingConnectionProvider.POOLING_PROVIDER); // provider
String dsDriver = pp.getStringProperty(PoolingConnectionProvider.DB_DRIVER); //  driver
String dsURL = pp.getStringProperty(PoolingConnectionProvider.DB_URL); // URL


// 这里就是我们注意的是使用 hikaricp / c3p0 ,注意不是写全名,
// we load even these "core" providers by class name in order to avoid a static dependency on
// the c3p0 and hikaricp libraries
if(poolingProvider != null && poolingProvider.equals(PoolingConnectionProvider.POOLING_PROVIDER_HIKARICP)) {
    cpClass = "org.quartz.utils.HikariCpPoolingConnectionProvider";
}
else {
    cpClass = "org.quartz.utils.C3p0PoolingConnectionProvider";
}
复制代码

继续

Constructor constructor = loadHelper.loadClass(cpClass).getConstructor(Properties.class);
cp = (ConnectionProvider) constructor.newInstance(pp.getUnderlyingProperties());
复制代码

继续走 HikariCpPoolingConnectionProvider ,这个连接池性能高 , 比C3P0强一点 , 这里很显然一堆配置

public HikariCpPoolingConnectionProvider(Properties config) throws SchedulerException, SQLException {
    PropertiesParser cfg = new PropertiesParser(config);
    initialize(
            cfg.getStringProperty(DB_DRIVER),
            cfg.getStringProperty(DB_URL),
            cfg.getStringProperty(DB_USER, ""),
            cfg.getStringProperty(DB_PASSWORD, ""),
            cfg.getIntProperty(DB_MAX_CONNECTIONS, DEFAULT_DB_MAX_CONNECTIONS),
            cfg.getStringProperty(DB_VALIDATION_QUERY),
            cfg.getIntProperty(DB_DISCARD_IDLE_CONNECTIONS_SECONDS, 0));
}
复制代码

这里就实例化好了 数据库连接池 ,

其实他的整体设计很好 , 第一我们作为存储方 , 不用管理连接方 ,所以提供了两个, 一个是 org.quartz.spi.JobStore ,然后就是负责连接的是 DBConnectionManager , 然后这个管理者管理的是 ConnectionProvider , 所以很好的解耦, 这个设计很棒 . 拓展点多 ,

其实问题还没有结束, 如果我们直接通过 ...

org.quartz.scheduler.instanceName=MyScheduler
org.quartz.threadPool.threadCount=4
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX


org.quartz.dataSource.jpa.provider=hikaricp
org.quartz.dataSource.jpa.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.jpa.URL=jdbc:mysql://localhost:3306/quartz?useSSL=false
org.quartz.dataSource.jpa.user=root
org.quartz.dataSource.jpa.password=123456
org.quartz.dataSource.jpa.maxConnections=5
复制代码

一我们目前的认真案例来说写应该这么写, 足够了 ,因此启动 ///

结果报错, 说 :

org.quartz.SchedulerConfigException: DataSource name not set.
复制代码

我们因此去到了 ...

public void initialize(ClassLoadHelper loadHelper,
        SchedulerSignaler signaler) throws SchedulerConfigException {

    // 为啥么设置 , 继续找
    if (dsName == null) { 
        throw new SchedulerConfigException("DataSource name not set."); 
    }
    //// .... 
}
复制代码

我们找到字段的方法 , 然后通过查找是否被调用, 发现没有被调用过 ? ,怎么办, 我们在这里打断点, 一定有调用,

public void setDataSource(String dsName) {
    this.dsName = dsName;
}
复制代码

发现是在 org.quartz.impl.StdSchedulerFactory#setBeanProps 这里被调用的, 原来是反射哇 ,茅舍顿开, 继续

tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
// 这里调用, jobstore
setBeanProps(js, tProps);
复制代码

很显然是 org.quartz.jobStore 这里设置两个 tablePrefix 和 dsName 其实还有很多可以设置 , 只要是遵循了他的使用原则 . 基本就是 比如说 setDataSource 我们就配置中写 , org.quartz.jobStore.dataSource , 主要第一个字母小写 . 就是这些. ... 所以问题处理了, 有些时候么有直接被调用基本上使用的是反射.

所以我们配置文件是?

org.quartz.scheduler.instanceName=MyScheduler
org.quartz.threadPool.threadCount=4
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX


// 这里才算完整了..... 
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = quartz
org.quartz.dataSource.quartz.provider=hikaricp
org.quartz.dataSource.quartz.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.quartz.URL=jdbc:mysql://localhost:3306/quartz?useSSL=false
org.quartz.dataSource.quartz.user=root
org.quartz.dataSource.quartz.password=123456
org.quartz.dataSource.quartz.maxConnections=5
复制代码

表我就直接告诉大家了, 这个可以去官网找, 他不会自动建表, 像JPA那样 ,所以很坑这点,

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


commit;
复制代码
原文  https://juejin.im/post/5e8573fde51d4546bc38cc8a
正文到此结束
Loading...