欢迎进入Wiki » FAQ » 集群环境下,如何使用锁(Locker)来避免计划任务(Quartz)执行冲突?

集群环境下,如何使用锁(Locker)来避免计划任务(Quartz)执行冲突?

在2013-11-25 16:18上被李小翔修改
评论 (0) · 附件 (0) · 记录 · 信息

默认情况下:

  • Quartz 任务都是保存在内存中的,因此在集群环境下,不可避免的会出现相同的任务同时执行的情况;
  • 如果在 Config.groovy 中启用 quartz.jdbcStore 将任务保存到数据库,并设置任务(XxxxJob)的属性 concurrent 为 false,则可以支持集群环境下不允许同时运行多个相同的任务;
  • 进一步,如果多个应用设置不同的实例名称(quartz.props.scheduler.instanceName),则可以支持同名应用的集群不允许同时运行多个相同的任务,而不同应用的集群可以同时运行相同的任务。

但是,在实际运行过程中,效果却不甚理想,总是出现一些莫名的错误。

所以在常规的 J2EE 应用开发中,通常是把 quartz 任务单独作为一个 war 包部署,甚至部署到独立的 jvm 中,以避免各种冲突。

BroFramework 中提供了一套基于数据库的Lock API,使用这套API,可以灵活的处理 Quartz 中的任务执行冲突,并且实现各种 Quartz 原本无法实现的策略。

考虑下面的几个场景中,应用 Foo 和应用 Bar 中,有一个同名的 FoobarJob,这两个应用使用同一套 BroFramework 数据库:

1、希望同时只有一个 FoobarJob 执行:

import bropen.framework.core.Locker;

class FoobarJob {
   /**
     * 执行定时任务
     */

   def execute() {
        String lockKey = "FoobarJob";    // 按任务名加锁
       if ( !Locker.lockExit(lockKey, false) ) return;    // 加锁失败则直接退出
       try {
           // 任务执行代码
       } finally {
            Locker.unlock(lockKey, false);    // 执行完毕,解锁
       }
   }
   
}

2、希望 Foo 和 Bar 的多个集群节点中只有一个 FoobarJob 执行,但是Foo、Bar两个应用可以同时运行 FoobarJob

import bropen.framework.core.Locker;
import bropen.framework.core.security.DomainApplication;

class FoobarJob {
   /**
     * 执行定时任务
     */

   def execute() {
        String lockKey = "FoobarJob_" + DomainApplication.current().id;    // 按任务名和应用加锁
       if ( !Locker.lockExit(lockKey, false) ) return;    // 加锁失败则直接退出
       try {
           // 任务执行代码
       } finally {
            Locker.unlock(lockKey, false);    // 执行完毕,解锁
       }
   }
   
}

3、如果有多个域(启用域管理后),可以在 lockKey 中添加域的ID(Domain.current().id)做为锁的关键字,以实现一个域的多个应用中,只允许运行一个同名的任务。

4、如果有两个任务会争用相同的资源,可以为两个任务设置相同的 lockKey,并且可以使用 lock 方法来代替 lockExit 方法,实现等待功能,如:

Locker.lock(lockKey, false, null, -1) // 一直等待

5、如果 FoobarJob 执行时,有自动发出邮件、生成待办等操作(比如每周三凌晨,给所有员工发出一个工作周报的待办任务),而且执行时长不等、执行间隔比较长(如大于半个小时),则在 execute 的 finally 中不解锁,而是让锁自动超时失效,以避免集群间极小的时间差导致任务多次重复执行,如:

def execute() {
    String lockKey = "FoobarJob";    // 按任务名加锁
   if ( !Locker.lockExit(lockKey, false, 30*60*1000) ) return;    // 加锁失败则直接退出,否则加锁并设置超时时间为 30 分钟(默认)
   // 任务执行代码,执行完后不解锁,让其自动超时失效
   ....
}
标签: BroFramework quartz
在2013-11-25 16:17上被李小翔创建

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号