如何用Spring实现集群环境下的定时任务
2017-06-06 09:20
381 查看
定时任务的实现方式有多种,例如JDK自带的Timer+TimerTask方式,spring 3.0以后的调度任务(Scheduled Task),Quartz等。
Timer+TimerTask是最基本的解决方案,但是比较远古了,这里不再讨论。Spring自带的Scheduled
Task是一个轻量级的定时任务调度器,支持固定时间(支持cron表达式)和固定时间间隔调度任务,支持线程池管理。以上两种方式有一个共同的缺点,那就是应用服务器集群下会出现任务多次被调度执行的情况,因为集群的节点之间是不会共享任务信息的,每个节点上的任务都会按时执行。Quartz是一个功能完善的任务调度框架,特别牛叉的是它支持集群环境下的任务调度,当然代价也很大,需要将任务调度状态序列化到数据库。Quartz框架需要10多张表协同,配置繁多,令人望而却步...
经过折中考虑,还是选择了Spring的Scheduled Task来实现定时任务。如下:
1. Spring配置文件application-context.xml中添加task命名空间和描述。
[html] view
plain copy
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">
2. 添加调度器和线程池声明。
[html] view
plain copy
<task:executor id="taskExecutor" pool-size="10" />
<task:annotation-driven executor="taskExecutor" />
3. 实现调度方法。基本结构如下:
[html] view
plain copy
package com.netease.yx.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class ScheduledService {
@Scheduled(cron = "0 0 5 * * *")
public void build() {
System.out.println("Scheduled Task");
}
}
@Scheduled注解支持秒级的cron表达式,上述声明表示每天5点执行build任务。
前文已经提过,这种方式在单台应用服务器上运行没有问题,但是在集群环境下,会造成build任务在5点的时候运行多次,遗憾的是,Scheduled Task在框架层面没有相应的解决方案,只能靠程序员在应用级别进行控制。
如何控制?
1. 无非是一个任务互斥访问的问题,声明一把全局的“锁”作为互斥量,哪个应用服务器拿到这把“锁”,就有执行任务的权利,未拿到“锁”的应用服务器不进行任何任务相关的操作。
2.这把“锁”最好还能在下次任务执行时间点前失效。
在项目中我将这个互斥量放在了Redis缓存里,1小时过期,这个过期时间是由任务调度的间隔时间决定的,只要小于两次任务执行时间差,大于集群间应用服务器的时间差即可。
完整定时任务类如下:
[html] view
plain copy
package com.netease.yx.service;
import javax.annotation.Resource;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import com.netease.yx.service.ICacheService;
@Service
public class ScheduledService {
@Resource
private ICacheService cache = null;
private static String CACHE_LOCK = "cache_lock";
private static int EXPIRE_PERIOD = (int)DateUtils.MILLIS_PER_HOUR / 1000;
@Scheduled(cron = "0 0 5 * * *")
public void build() {
if (cache.get(CACHE_LOCK) == null) {
cache.set(CACHE_LOCK, true, EXPIRE_PERIOD);
doJob();
}
}
}
Timer+TimerTask是最基本的解决方案,但是比较远古了,这里不再讨论。Spring自带的Scheduled
Task是一个轻量级的定时任务调度器,支持固定时间(支持cron表达式)和固定时间间隔调度任务,支持线程池管理。以上两种方式有一个共同的缺点,那就是应用服务器集群下会出现任务多次被调度执行的情况,因为集群的节点之间是不会共享任务信息的,每个节点上的任务都会按时执行。Quartz是一个功能完善的任务调度框架,特别牛叉的是它支持集群环境下的任务调度,当然代价也很大,需要将任务调度状态序列化到数据库。Quartz框架需要10多张表协同,配置繁多,令人望而却步...
经过折中考虑,还是选择了Spring的Scheduled Task来实现定时任务。如下:
1. Spring配置文件application-context.xml中添加task命名空间和描述。
[html] view
plain copy
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">
2. 添加调度器和线程池声明。
[html] view
plain copy
<task:executor id="taskExecutor" pool-size="10" />
<task:annotation-driven executor="taskExecutor" />
3. 实现调度方法。基本结构如下:
[html] view
plain copy
package com.netease.yx.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class ScheduledService {
@Scheduled(cron = "0 0 5 * * *")
public void build() {
System.out.println("Scheduled Task");
}
}
@Scheduled注解支持秒级的cron表达式,上述声明表示每天5点执行build任务。
前文已经提过,这种方式在单台应用服务器上运行没有问题,但是在集群环境下,会造成build任务在5点的时候运行多次,遗憾的是,Scheduled Task在框架层面没有相应的解决方案,只能靠程序员在应用级别进行控制。
如何控制?
1. 无非是一个任务互斥访问的问题,声明一把全局的“锁”作为互斥量,哪个应用服务器拿到这把“锁”,就有执行任务的权利,未拿到“锁”的应用服务器不进行任何任务相关的操作。
2.这把“锁”最好还能在下次任务执行时间点前失效。
在项目中我将这个互斥量放在了Redis缓存里,1小时过期,这个过期时间是由任务调度的间隔时间决定的,只要小于两次任务执行时间差,大于集群间应用服务器的时间差即可。
完整定时任务类如下:
[html] view
plain copy
package com.netease.yx.service;
import javax.annotation.Resource;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import com.netease.yx.service.ICacheService;
@Service
public class ScheduledService {
@Resource
private ICacheService cache = null;
private static String CACHE_LOCK = "cache_lock";
private static int EXPIRE_PERIOD = (int)DateUtils.MILLIS_PER_HOUR / 1000;
@Scheduled(cron = "0 0 5 * * *")
public void build() {
if (cache.get(CACHE_LOCK) == null) {
cache.set(CACHE_LOCK, true, EXPIRE_PERIOD);
doJob();
}
}
}
相关文章推荐
- 如何用Spring实现集群环境下的定时任务
- 如何用Spring实现集群环境下的定时任务
- spring boot 整合 quartz 集群环境 实现 动态定时任务配置【原】
- Spring1.1.1+quartz1.8.6实现集群环境下的定时任务
- 集群环境下如何防止定时任务重复执行?
- 万物生长 项目jar包越少越好,不然会导致eclipse进入断点延迟高,很慢. Spring+quartz 实现定时任务job集群配置
- Spring+quartz实现定时任务集群
- 集群环境下如何防止定时任务重复执行?
- [转]ssh中如何实现定时任务(spring对quartz的支持)
- Quartz - Spring集成Quartz实现集群的定时任务
- Spring环境下实现定时执行任务
- Spring 整合Quartz 2实现定时任务五:集群、分布式架构实现探讨
- Spring 整合Quartz 2实现定时任务五:集群、分布式架构实现探讨
- spring如何实现定时任务
- Spring 整合Quartz 2实现定时任务五:集群、分布式架构实现探讨
- Spring+quartz 实现定时任务job集群配置【原】
- 如何使用spring 定时调度 【 Spring+Quartz实现定时任务 】
- 如何实现WebSphere Application Server 6集群环境下的定时服务
- Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)
- Spring+Quartz实现定时任务的配置方法