Related: Quartz Clustering - triggers duplicated when the server starts
我正在使用Quartz Scheduler来管理基于java的集群环境中的预定作业。在任何给定时间,集群中都有一些节点,它们都运行Quartz,由postgresql数据库中的数据存储支持,所有节点都连接到该数据库。
初始化实例时,它会尝试通过执行以下代码在Quartz数据存储中创建或更新作业和触发器:
private void createOrUpdateJob(JobKey jobKey, Class<? extends org.quartz.Job> clazz, Trigger trigger) throws SchedulerException {
JobBuilder jobBuilder = JobBuilder.newJob(clazz).withIdentity(jobKey);
if (!scheduler.checkExists(jobKey)) {
// if the job doesn't already exist, we can create it, along with its trigger. this prevents us
// from creating multiple instances of the same job when running in a clustered environment
scheduler.scheduleJob(jobBuilder.build(), trigger);
log.error("SCHEDULED JOB WITH KEY " + jobKey.toString());
} else {
// if the job has exactly one trigger, we can just reschedule it, which allows us to update the schedule for
// that trigger.
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
if (triggers.size() == 1) {
scheduler.rescheduleJob(triggers.get(0).getKey(), trigger);
return;
}
// if for some reason the job has multiple triggers, it's easiest to just delete and re-create the job,
// since we want to enforce a one-to-one relationship between jobs and triggers
scheduler.deleteJob(jobKey);
scheduler.scheduleJob(jobBuilder.build(), trigger);
}
}
这种方法解决了许多问题:
这一切都很好,但是当两个实例在同一时间启动时,我担心潜在的竞争条件。因为群集中的所有节点都不会对此代码进行全局锁定,如果两个实例同时联机,我最终可能会遇到重复的作业或触发器,这会破坏此代码的重点。
是否有在群集环境中自动定义Quartz作业和触发器的最佳实践?或者我是否需要设置自己的锁?
答案 0 :(得分:1)
我不确定在Quartz中是否有更好的方法可以做到这一点。但是如果您已经在使用Redis或Memcache,我建议让所有实例对一个众所周知的密钥执行atomic increment。如果您粘贴的代码每小时每个群集只能运行一个作业,则可以执行以下操作:
long timestamp = System.currentTimeMillis() / 1000 / 60 / 60;
String key = String.format("%s_%d", jobId, timestamp);
// this will only be true for one instance in the cluster per (job, timestamp) tuple
bool shouldExecute = redis.incr(key) == 1
if (shouldExecute) {
// run the mutually exclusive code
}
时间戳为您提供了一个移动窗口,在该窗口中,作业可以竞争执行此作业。
答案 1 :(得分:0)
我(几乎)遇到了同样的问题:如何在群集环境中的每个软件版本中创建一次触发器和作业。我通过在启动期间将其中一个群集节点指定为主节点并让它重新创建Quartz作业来解决问题。引导节点是首先成功地将正在运行的软件的git修订号插入数据库的节点。其他节点使用由主导节点创建的Quartz配置。以下是完整的解决方案:https://github.com/perttuta/quartz