分布式定时任务

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        ">
View Code

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原子操作。

以上是关于分布式定时任务的主要内容,如果未能解决你的问题,请参考以下文章

什么是分布式定时任务框架?

springcloud技术栈系列4:分布式定时任务

几种主流的分布式定时任务,你知道哪些?

XXL-Job分布式任务调度框架-- 定时任务注册案例2

SpringBoot定时任务 - 开箱即用分布式任务框架xxl-job

分布式定时任务原理以及解决方案-指定时间执行定时任务