Spring Scheduler 使用

本文详细介绍了Spring框架中的定时任务功能,包括TaskExecutor和TaskScheduler的概念及其配置方式,并提供了使用XML和注解配置的例子。此外还深入探讨了Cron表达式的使用方法及多实例部署时的注意事项。

参考:http://qinghua.github.io/spring-scheduler/

概念

    spring scheduler 包含两个概念,任务(Task) 和 运行任务的框架(TaskExecutor/TaskScheduler)。TaskExecutor 是任务执行器,允许我们异步执行多个任务;TaskScheduler 是任务调度器,来运行定时任务;触发器Trigger 可以决定定时任务是否该运行了,最常用的是CronTrigger。

    spring 内置了多种类型的 TaskExecutor 和TaskScheduler,方便用户根据不同的业务场景选择。

运行与配置

用xml 配置的话:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">

    <task:scheduler id="myScheduler"/>

    <task:scheduled-tasks scheduler="myScheduler">
        <task:scheduled ref="doSomethingTask" method="doSomething" cron="0 * * * * *"/>
    </task:scheduled-tasks>
</beans>

注意 doSomethingTask 以及对应的方法都必须存在

用注解的话

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class DoSomethingTask {

    @Scheduled(cron="0 * * * * *")
    public void doSomething() {
        System.out.println("do something");
    }
}

Cron

参考官方文档:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/crontrigger

Cron表达式由6~7项组成,中间用空格分开。从左到右依次是:秒、分、时、日、月、周几、年(可省略)。值可以是数字,也可以是以下符号:
*:所有值都匹配
?:无所谓,不关心,通常放在“周几”里
,:或者
/:增量值
-:区间


下面举几个例子,看了就知道了:
0 * * * * *:每分钟(当秒为0的时候)
0 0 * * * *:每小时(当秒和分都为0的时候)
*/10 * * * * *:每10秒
0 5/15 * * * *:每小时的5分、20分、35分、50分
0 0 9,13 * * *:每天的9点和13点
0 0 8-10 * * *:每天的8点、9点、10点
0 0/30 8-10 * * *:每天的8点、8点半、9点、9点半、10点
0 0 9-17 * *  MON-FRI:每周一到周五的9点、10点…直到17点(含)
0 0 0 25 12 ?: 每年12约25日圣诞节的0点0分0秒(午夜)
0 30 10 * * ?  2016:2016年每天的10点半


其中的?在用法上其实和*是相同的。但是*语义上表示全匹配,而?并不代表全匹配,而是不关心。比如对于0 0 0 5 8 ? 2016来说,2016年8月5日是周五,?表示我不关心它是周几。而0 0 0 5 8 * 2016中的*表示周一也行,周二也行……语义上和2016年8月5日冲突了,你说谁优先生效呢。
不记得也没关系,记住Cron Maker也可以,它可以在线生成cron表达式。

配置技巧

不同的环境可能会配置不同的cron。

比如测试环境的cron 希望更频繁。

xml 配置:

<task:scheduler id="myScheduler"/>

<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="doSomethingTask" method="doSomething" cron="${cron_expression}"/>
    <task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${cron_expression}"/>
</task:scheduled-tasks>

java 的properties 文件支持空格:

cron_expression=0 * * * * *

注解的解决方法:

	
@Scheduled(cron = "${cron_expression}")

注意事项

同一个task,如果前一个没有跑完,后面一个就不会触发,这没有问题。但是不同的task 也不能同时运行就不太合理了。

这个是由于schedule 的默认线程数被设置为1 的缘故。

解决方法1:

如下配置pool-size,但这样会导致同一个task 前一个还没有跑完后面又被触发的问题

<task:scheduler id="scheduler" pool-size="2" />

解决方法2:

让任务分别运行在不同的scheduler中

<task:scheduler id="myScheduler1"/>
<task:scheduler id="myScheduler2"/>

<task:scheduled-tasks scheduler="myScheduler1">
    <task:scheduled ref="doSomethingTask" method="doSomething" cron="${0 * * * * *}"/>
</task:scheduled-tasks>

<task:scheduled-tasks scheduler="myScheduler2">
    <task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${0 * * * * *}"/>
</task:scheduled-tasks>

多实例

若是scheduler与web配置在一起,在高可用的情况下,如果有多个web容器实例,scheduler会在多个实例上同时运行。

解决方法1:部署的时候,针对不同实例,使用不同的配置。比如tomcat_1打开scheduler,tomcat_2关闭。带来的问题是:

  • 增加部署成本。
  • 要是tomcat_1挂了,scheduler就不能运行了,高可用落空。

解决方法2:在task的基类加入一些逻辑,当开始运行时,将状态(运行机器的IP、时间等)写入数据库、缓存或者zk,运行结束时重置状态。其它实例看到有这样的数据,就直接返回。带来的问题是:

  • 需要所有实例上的机器时间同步,不然一个刚结束另一个才开始,状态的维护就没有用了。
  • 一定要保证结束运行后将状态重置,否则下一个运行周期,所有的task都会返回的。实在不行还得写一个task做这个事。
  • 因为读写状态并非原子操作,偶尔也会发生task同时运行的事。

解决方法3:将scheduler与web分开。这样还能避免后台任务影响web端。带来的问题是:

  • 增加部署成本。
  • scheduler的高可用需要重新考虑。

转载于:https://my.oschina.net/pingjiangyetan/blog/1590542

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值