分布式定时任务
Posted ssskkk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式定时任务相关的知识,希望对你有一定的参考价值。
JDK中Timer类
java.util.Timer定时器实际上是一个单线程,定时调度所拥有的TimerTask任务。
TimerTask类是一个定时任务类,实现了Runnable接口,而且是一个抽象类,需要定时执行的任务都需要重写它的run方法。
TImer类的缺陷
1)单线程,如果存在多个任务,某个任务执行时间过长,就会导致任务时间延迟。
2)异常终止,如果TimerTask抛出了未捕获的异常,则也会导致Timer线程终止,已经被安排但尚未执行的TimerTask也不会再执行了。
3)执行周期任务时依赖系统时间:如果当前系统时间发生了变化,会出现一些执行上的变化。
ScheduledExecutorServiceh和Spring Schedule
jdk1.5推出了基于线程池设计的ScheduledExecutorService,其设计思想是:每个被调度的任务都会由线程池中的一个线程去执行,因此任务是并发的,相互之间不会受到干扰。
其实除了自己可以实现定时任务,还可以通过使用Spring实现定时任务。
1.在Spring配置文件头中添加命名空间及描述(第9行、14和15行)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xmlns:jpa="http://www.springframework.org/schema/data/jpa" 8 xmlns:jaxws="http://cxf.apache.org/jaxws" 9 xmlns:task="http://www.springframework.org/schema/task" 10 xsi:schemaLocation="http://www.springframework.org/schema/beans 11 http://www.springframework.org/schema/beans/spring-beans.xsd 12 http://www.springframework.org/schema/context 13 http://www.springframework.org/schema/context/spring-context.xsd 14 http://www.springframework.org/schema/task 15 http://www.springframework.org/schema/task/spring-task-3.0.xsd 16 http://www.springframework.org/schema/aop 17 http://www.springframework.org/schema/aop/spring-aop.xsd 18 http://www.springframework.org/schema/tx 19 http://www.springframework.org/schema/tx/spring-tx.xsd 20 http://www.springframework.org/schema/data/jpa 21 http://www.springframework.org/schema/data/jpa/spring-jpa.xsd 22 http://cxf.apache.org/jaxws 23 http://cxf.apache.org/schemas/jaxws.xsd 24 ">
2.添加支持注解的配置 <task:annotation-driven>
3.定义任务,代码如下
@Override @Scheduled(cron="0/20 * * * * ? ") //每20秒执行一次 public void testAop() { System.out.println("excute Service***********"); }
Cron表达式
Cron表达式的格式为"秒 分 时 日 月 周 年" 推荐大家使用一些工具生成Cron表达式http://www.pppet.net/
分布式定时任务
分布式场景下定时任务的一个问题就是:怎么让某一个定时任务在一个触发时刻上仅有一台服务器在运行。
1.只在一台服务器执行
可以指定所有的调度任务只在固定的单台服务器上执行,虽然该方法解决了重复执行的问题,但是存在明显的两个缺陷:
单点风险和资源分配不均衡
2.通过配置参数分散运行
可以创建一个配置项,其中包含执行的定时任务类名,这样可以在部署服务时手动分散定时任务到不同的服务器上。
该方法虽然解决了资源分配不均衡的问题,不过依然存在单点风险,同时增加了运维管理难度。
3.通过全局"锁"互斥运行
可以通过分布式锁来实现,当节点获取到锁就执行任务,在没有获取到锁时就不执行(抢占执行),这样就可以解决多节点重复执行任务的问题。可以使用Zookeeper、Redis或者数据库的方式来实现分布式锁。
接下来采用Redis来实现一个简单的分布式锁,示例代码如下。
@Autowired private StringRedisTemplate stringRedisTemplate; private static final String KEY="lock_hello"; public void doTask() { boolean lock=false; try { //获取锁 lock=stringRedisTemplate.opsForValue().setIfAbsent(KEY, "1"); if(!lock) { //获取不到锁,直接退出 return; } //设置超时,防止程序意外终止而导致key锁无法释放 stringRedisTemplate.expire(KEY, 5,TimeUnit.MINUTES); //to do something System.out.println("do task..."); } finally { //最终释放锁 stringRedisTemplate.delete(KEY); } }
在程序中调用setIfAbsent方法来获取锁,如果返回true,则说明该key值不存在,表示获取到了锁;
如果返回false,则说明该key值存在,已经有程序在使用这个key值,从而实现了分布式加锁的功能。serIfAbsent封装了Redis原生的SETNX原子操作。
以上是关于分布式定时任务的主要内容,如果未能解决你的问题,请参考以下文章