随笔记

平凡人平凡路,沉下心迈出步

0%

SpringBoot1Quartz集群模式

spring boot 1 如何使用Quartz cluster

maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency><!-- 数据源DataSource需要,可以使用其他组件替代 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring.boot.version}</version>
</dependency>

quartz.properties配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 是否使用properties作为数据存储
org.quartz.jobStore.useProperties=false
# 数据库中的表格命名前缀
org.quartz.jobStore.tablePrefix=QRTZ_
# 是否是一个集群,是不是分布式的任务
org.quartz.jobStore.isClustered=true
# 集群检查周期,单位毫秒。可以自定义缩短时间。 当某一个节点宕机的时候,其他节点等待多久后开始执行任务。
org.quartz.jobStore.clusterCheckinInterval=5000
# 单位毫秒, 集群中的节点退出后,再次检查进入的时间间隔。
org.quartz.jobStore.misfireThreshold=1000
# 事务隔离级别
org.quartz.jobStore.txIsolationLevelReadCommitted=true
# 存储的事务管理类型
#org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
# 使用的Delegate类型
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 集群的命名,一个集群要有相同的命名。
org.quartz.scheduler.instanceName=ClusterQuartz
# 节点的命名,可以自定义。 AUTO代表自动生成。
org.quartz.scheduler.instanceId=AUTO
# rmi远程协议是否发布
org.quartz.scheduler.rmi.export=false
# rmi远程协议代理是否创建
org.quartz.scheduler.rmi.proxy=false
# 是否使用用户控制的事务环境触发执行job。
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
# 指定默认线程池大小
org.quartz.threadPool.threadCount=50

相关quartz bean配置

  1. 装配SchedulerFactoryBean ,替换数据源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    @Configuration
    public class QuartzConfig {
    @Autowired
    private DataSource dataSource;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
    SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    schedulerFactoryBean.setDataSource(dataSource); // 使用application.properties 中的数据源
    schedulerFactoryBean.setOverwriteExistingJobs(true);
    // schedulerFactoryBean.setJobFactory(jobFactory);
    schedulerFactoryBean.setQuartzProperties(quartzProperties());
    schedulerFactoryBean.setSchedulerName("quartz-cluster-scheduler");
    schedulerFactoryBean.setStartupDelay(2);// 延迟两秒启动
    schedulerFactoryBean.setAutoStartup(true);
    return schedulerFactoryBean;
    }

    private Properties quartzProperties() throws IOException {//解析quartz配置
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
    propertiesFactoryBean.afterPropertiesSet();
    Properties properties = propertiesFactoryBean.getObject();
    return properties;
    }
    }
  2. 自定义Job类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Slf4j
@DisallowConcurrentExecution //重要
@PersistJobDataAfterExecution //存储任务数据
public class CustomJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取参数
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
// 业务逻辑 ...
log.info("------springbootquartzonejob执行" + jobDataMap.get("name").toString() + "###############" + jobExecutionContext.getTrigger());

}
}
  1. 封装addJob/deleteJob方便使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

@Service
@Slf4j
public class QuartzService {
@Autowired
private Scheduler scheduler;

/**
* 增加一个job
*
* @param jobClass 任务实现类
* @param jobName 任务名称(建议唯一)
* @param jobGroupName 任务组名
* @param jobTime 时间表达式 (如:0/5 * * * * ? )
* @param jobData 参数
*/
public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
// 任务名称和组构成任务key
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
.build();
// 设置job参数
if (jobData != null && jobData.size() > 0) {
jobDetail.getJobDataMap().putAll(jobData);
}
// 定义调度触发规则
// 使用cornTrigger规则
// 触发器key
// Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
// .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
// .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build();
// // 把作业和触发器注册到任务调度中
// scheduler.scheduleJob(jobDetail, trigger);

// 设置定时任务执行方式
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobTime)
// MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 立即执行一次
// MISFIRE_INSTRUCTION_DO_NOTHING 不执行,等待下次触发
.withMisfireHandlingInstructionDoNothing();
// 构建触发器trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName).withSchedule(scheduleBuilder).build();

scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 删除任务一个job
*
* @param jobName 任务名称
* @param jobGroupName 任务组名
*/
public void deleteJob(String jobName, String jobGroupName) {
try {
scheduler.deleteJob(new JobKey(jobName, jobGroupName));
} catch (Exception e) {
e.printStackTrace();
}
}

}
  1. 外部操作定时器的地方只需要@Autowired private QuartzService quartzService; 注入service 按需调用即可。

Quartz 调度原理

  1. Scheduler 任务调度控制器 (StdScheduler)

    管理 Trigger 和 Job

  2. Trigger 任务调度单元

    CronTrigger 可以通过 Cron 表达式规定任务触发规则

  3. SimpleTrigger 规定任务执行几次,每次的时间间隔,类似 SchedulerExecutor

    Job 调度任务,用于定义你的业务任务具体执行过程

  4. 一个 Job 可以对应多个 Trigger,一个 Trigger 只能对应一个 Job

Quartz cluster 实现原理

集群的概念:是指在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡设备对外提供服务。
分布式的概念:是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务。

quartz的实现核心:

  1. 集群调度相关类

    JobStoreSuppoœrt 数据库存储任务信息实现
    StdRowLockSemaphore 数据库行锁实现

  2. 表QRTZ_SCHEDULER_STATE维护失效实例(LAST_CHECKIN_TIME 心跳时间)信息

  3. 表QRTZ_LOCKS实现数据库行锁

通过 select for update 语句会阻塞其他同样针对这一行的 select 语句,直到该 session commit 或 rollback

  1. 更多原理查看此连接 QuartzCluster实现原理

源码分析

  1. 内存模式启动信息(不使用db持久化任务)
    avater
    说明了使用的JobStore,线程池及大小

  2. DB存储方式并启用集群
    avater

  3. 当我们指定了DataSource后,Quartz启动后会把org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX 覆盖为LocalDataSourceJobStore,核心逻辑如下

    1
    2
    3
    4
    CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps);
    if (this.dataSource != null) {
    mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());
    }

Quartz 初始化过程

  1. 配置SchedulerFactoryBean org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet
  2. 执行 org.springframework.scheduling.quartz.SchedulerFactoryBean.prepareSchedulerFactory
  3. 然后在 次方法中会初始化 org.springframework.scheduling.quartz.SchedulerFactoryBean.initSchedulerFactory
  4. 会先准备配置然后做org.springframework.scheduling.quartz.SchedulerFactoryBean.prepareScheduler创建真正的Scheduler。
  5. 完成创建后会调用org.quartz.impl.StdSchedulerFactory.instantiate()进行其他组件初始化。

misfire 说明

详细分析在这个地址
https://segmentfault.com/a/1190000015492260,
解决我测试中发现的misfire问题(kill 进程,等待一定时间后再次拉起服务,依然会再次触发几次任务),主要是这个计算公式

调度线程会一次性拉取距离现在,一定时间窗口内的,一定数量内的,即将触发的trigger信息。那么,时间窗口和数量信息如何确定呢,我们先来看一下,以下几个参数:
idleWaitTime: 默认30s,可通过配置属性org.quartz.scheduler.idleWaitTime设置。
availThreadCount:获取可用(空闲)的工作线程数量,总会大于1,因为该方法会一直阻塞,直到有工作线程空闲下来。
maxBatchSize:一次拉取trigger的最大数量,默认是1,可通过org.quartz.scheduler.batchTriggerAcquisitionMaxCount改写
batchTimeWindow:时间窗口调节参数,默认是0,可通过org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow改写
misfireThreshold: 超过这个时间还未触发的trigger,被认为发生了misfire,默认60s,可通过org.quartz.jobStore.misfireThreshold设置。
调度线程一次会拉取NEXT_FIRE_TIME小于(now + idleWaitTime +batchTimeWindow),大于(now - misfireThreshold)的,min(availThreadCount,maxBatchSize)个triggers,默认情况下,会拉取未来30s,过去60s之间还未fire的1个trigger。随后将这些triggers的状态由WAITING改为ACQUIRED,并插入fired_triggers表。

​ 这样子通过调整配置中的

#单位毫秒, 集群中的节点退出后,再次检查进入的时间间隔 org.quartz.jobStore.misfireThreshold=1000 到一个较小值,这样MISFIRE_INSTRUCTION_DO_NOTHING 就能够达到我想要的,错过就不执行,等待下一次执行时机再触发的目的。

quartz.properties

1
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

指定了事务类型。其initialize方法调用的是父类org.quartz.impl.jdbcjobstore.JobStoreSupport.initialize()
在方法中指定了使用的是什么锁实现方式,如下图

avater

avatar

LocalDataSourceJobStore的初始化方式同理,也是其中的initialize方法。

此次架构升级注意点

  1. 之前使用的是内存保持调度信息的方式,所已在代码中有一个进程拉起后手动添加
    任务的动作com.enmotech.mozhe.sys.service.BatchTaskRunner。当升级为集群模式的时候,由于信息是已经存储好了,
    quartz会自动拉起所有定时器,不用手动操作,则需要屏蔽addJob相关动作。

  2. 定时器相关的功能,如果有停用、终止动作,最好同步做deleteJob操作,避免相同任务存在多个。

  3. 使用Spring 自身提供的@EnableScheduling 定时器机制,需要替换为Quartz的方式来做

SpringBoot 集成QuartzCluster方法

  • 配置方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    server:
    port: 8080

    spring:
    profiles:
    active: mysqlDB #切换指定的数据源.

    # quartz 调度器配置
    quartz:
    job-store-type: jdbc #数据持久化方式,可选值:MEMORY、JDBC
    auto-startup: true #初始化后是否自动启动计划程序,默认为 true
    overwrite-existing-jobs: false #配置的作业是否应覆盖现有的作业定义
    scheduler-name: quartzSchedulerV2 # 计划程序的名称
    startup-delay: 2s #初始化完成后启动计划程序的延迟时间,默认为 0 秒
    wait-for-jobs-to-complete-on-shutdown: false # 关闭时是否等待正在运行的作业完成

    #对于 Quartz 自带的配置,即可以使用 quartz 自己的 quartz.properties 配置文件进行配置,也可以直接配置在 properties 属性下,它是一个 map
    #quartz 完整配置:https://wangmaoxiong.blog.csdn.net/article/details/105057405#quartz.properties%20%E4%B8%8E%20QuartzProperties%20%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9
    properties:
    org:
    quartz:
    scheduler:
    instanceName: quartzSchedulerV2
    instanceId: AUTO

    jobStore:
    #如果不需要将调度命令(例如添加和删除triggers)绑定到其他事务,那么可以通过使用 JobStoreTX 管理事务
    class: org.quartz.impl.jdbcjobstore.JobStoreTX
    #设置数据库驱动代理,StdJDBCDelegate 是一个使用 JDBC 代码来执行其工作的代理. 其他代理可以在"org.quartz.impl.jdbcjobstore“包或其子包中找到
    driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    #通知 JobStore 使用的表前缀
    tablePrefix: QRTZ_
    isClustered: true
    clusterCheckinInterval: 5000
    misfireThreshold: 2000
    threadPool:
    class: org.quartz.simpl.SimpleThreadPool #quartz 使用的线程池类型,org.quartz.spi.ThreadPool
    threadCount: 15 #线程池中的线程总个数,表示最多可以同时执行的个任务/作业个数
    threadPriority: 5 #线程优先级
    threadsInheritContextClassLoaderOfInitializingThread: true #线程继承初始化线程的上下文类加载器



    ---
    spring:
    profiles: mysqlDB
    datasource:
    username: root
    password: 123456
    # spring boot 2.1.5 搭配 mysql 驱动 8.0.16,高版本 mysql 驱动的 driver-class-name 值要带 cj;url 值要带时区 serverTimezone
    url: jdbc:mysql://127.0.0.1:3306/training?characterEncoding=UTF-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
  • 关键bean声明方式

    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    public class BaseConfig {

    @Bean
    public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
    return schedulerFactoryBean.getScheduler();
    }
    }
  • 其他结构封装参考SpringBoot1的集成方式

工具类封装示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
@Service
@Slf4j
public class QuartzServices {
@Autowired
private Scheduler scheduler;

/**
* 注意,只封装了jobName/jobGroup/jobClass 无具体执行trigger信息,调用时,外部灵活指定
* <p>
* <br/>
*
* @param jobName
* @param jobGroup
* @param clazz
* @return
* @see JobInitServices#initSlowQueryScheduler
*/
public QuartzBean buildQuartzBean(String jobName, String jobGroup, Class<?> clazz) {
Assert.notNull(jobName, "任务名不能为空");
Assert.notNull(clazz, "任务类不能为空");
QuartzBean quartzBean = new QuartzBean();
quartzBean.setJobClass(clazz.getName());
quartzBean.setJobName(jobName);
quartzBean.setJobGroup(jobGroup); //默认分组
return quartzBean;
}

/**
* 构建cron表达式job(默认分组)
*
* @param jobName
* @param clazz
* @param cron
* @return
*/
public QuartzBean buildQuartzBean(String jobName, Class<?> clazz, String cron) {
Assert.notNull(jobName, "任务名不能为空");
Assert.notNull(clazz, "任务类不能为空");
Assert.notNull(cron, "Cron表达式不能为空");
QuartzBean quartzBean = new QuartzBean();
quartzBean.setJobClass(clazz.getName());
quartzBean.setJobName(jobName);
quartzBean.setJobGroup(null); //默认分组
quartzBean.setCronExpression(cron);
return quartzBean;
}

/**
* 构建cron表达式job
*
* @param jobName
* @param jobGroup
* @param clazz
* @param cron
* @return
*/
public QuartzBean buildQuartzBean(String jobName, String jobGroup, Class<? extends Job> clazz, String cron) {
Assert.notNull(jobName, "任务名不能为空");
Assert.notNull(clazz, "任务类不能为空");
Assert.notNull(cron, "Cron表达式不能为空");
QuartzBean quartzBean = new QuartzBean();
quartzBean.setJobClass(clazz.getName());
quartzBean.setJobName(jobName);
quartzBean.setJobGroup(jobGroup);
quartzBean.setCronExpression(cron);
return quartzBean;
}

/**
* 构建一次性允许job
*
* @param jobName
* @param jobGroup
* @param clazz
* @param fixTimePoint
* @return
*/
public QuartzBean buildQuartzBean(String jobName, String jobGroup, Class<?> clazz, Date fixTimePoint) {
Assert.notNull(jobName, "任务名不能为空");
Assert.notNull(clazz, "任务类不能为空");
Assert.notNull(fixTimePoint, "任务时间不能为空");
QuartzBean quartzBean = new QuartzBean();
quartzBean.setJobClass(clazz.getName());
quartzBean.setJobName(jobName);
quartzBean.setJobGroup(jobGroup);
quartzBean.setCronExpression(QuartzUtils.getCron(fixTimePoint));
return quartzBean;
}

/**
* 构建一次性允许job(默认分组)
*
* @param jobName
* @param clazz
* @param fixTimePoint
* @return
*/
public QuartzBean buildQuartzBean(String jobName, Class<?> clazz, Date fixTimePoint) {
Assert.notNull(jobName, "任务名不能为空");
Assert.notNull(clazz, "任务类不能为空");
Assert.notNull(fixTimePoint, "任务时间不能为空");
QuartzBean quartzBean = new QuartzBean();
quartzBean.setJobClass(clazz.getName());
quartzBean.setJobName(jobName);
quartzBean.setJobGroup(null);//默认分组
quartzBean.setCronExpression(QuartzUtils.getCron(fixTimePoint));
return quartzBean;
}

/**
* 创建一个定时任务(默认cronSchedule调度)
*
* @param quartzBean
*/
public void createScheduleJob(final QuartzBean quartzBean) {
Assert.notNull(quartzBean.getCronExpression(), "定时任务Cron不能为空");
try {
//获取到定时任务的执行类 必须是类的绝对路径名称
// 构建触发器trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(quartzBean.getJobName(), quartzBean.getJobGroup())
.withSchedule(CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression())
.withMisfireHandlingInstructionDoNothing()).build();
createScheduleJob(quartzBean, trigger);
} catch (Exception e) {
log.error("创建定时任务出错", e);
}
}

public void createScheduleJob(final QuartzBean quartzBean, final Trigger trigger) {
try {
//获取到定时任务的执行类 必须是类的绝对路径名称
//定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
// 构建定时任务信息
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(quartzBean.getJobName(), quartzBean.getJobGroup()).build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (ClassNotFoundException e) {
log.error("创建定时任务出错,定时任务类路径出错:请输入类的绝对路径", e);
} catch (Exception e) {
log.error("创建定时任务出错", e);
}
}

/**
* 更新定时任务,使用cronSchedule调度
*
* @param quartzBean 定时任务信息类
*/
public void updateScheduleJob(final QuartzBean quartzBean) {
try {
Assert.notNull(quartzBean.getCronExpression(), "cron表达式不能为空");
// 获取到对应任务的触发器
TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName(), quartzBean.getJobGroup());
//设置定时任务执行方式
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//重新构建任务的触发器trigger
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null) {
log.error("not found trigger,jobName:{},jobGroup:{}", triggerKey.getName(), triggerKey.getGroup());
return;
}
trigger = trigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();

updateScheduleJob(quartzBean, trigger);
} catch (Exception e) {
log.error("更新定时任务出错", e);
}
}

/**
* 更新定时器任务
*
* @param quartzBean
* @param trigger 允许自定义触发方式
*/
public void updateScheduleJob(final QuartzBean quartzBean, final Trigger trigger) {
Assert.notNull(trigger, "触发器不能为空");
try {
// 获取到对应任务的触发器
TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName(), quartzBean.getJobGroup());
//重置对应的job
scheduler.rescheduleJob(triggerKey, trigger);
} catch (Exception e) {
log.error("更新定时任务出错", e);
}
}

/**
* 如果存在任务执行更新操作,如果不存在执行新增操作
*
* @param quartzBean
*/
public void updateOrCreateScheduleJob(final QuartzBean quartzBean, final Trigger trigger) {
if (checkExists(quartzBean.getJobName(), quartzBean.getJobGroup())) {
updateScheduleJob(quartzBean, trigger);
} else {
createScheduleJob(quartzBean, trigger);
}
}

public void updateOrCreateScheduleJob(final QuartzBean quartzBean) {
if (checkExists(quartzBean.getJobName(), quartzBean.getJobGroup())) {
updateScheduleJob(quartzBean);
} else {
createScheduleJob(quartzBean);
}
}

private boolean checkExists(final String jobName, String jobGroup) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
try {
return scheduler.checkExists(triggerKey);
} catch (SchedulerException e) {
log.error(e.getMessage(), e);
}
return false;
}


/**
* 根据定时任务名称从调度器当中删除定时任务
*
* @param jobName 定时任务名称
* @param jobGroup 定时任务分组名称
*/
public boolean deleteScheduleJob(final String jobName, String jobGroup) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
try {
return scheduler.deleteJob(jobKey);
} catch (Exception e) {
log.error("删除定时任务出错", e);
}
return false;
}
}