spring调度定时任务的方式
spring 定时器任务scheduled-tasks默认配置是单线程串行执行的,多个任务相当于串行。每个job都是等待上个执行完了才执行下一个job。这就造成了若某个任务执行时间过长,其他任务一直在排队,业务逻辑没有及时处理的问题。
单线程执行定时任务带来的问题
spring调度定时任务的方式就会导致:bTask会因为aTask的超时执行而延迟执行。
如下是scheduled定义了3个任务。
<task:scheduled-tasks >
<task:scheduled ref="myTask1" method="run" cron="0 0/59 10-23 * * ?"/>
<task:scheduled ref="myTask2" method="run" cron="0/10 * * * * ?"/>
<task:scheduled ref="myTask3" method="run" cron="0/10 * * * * ?"/>
</task:scheduled-tasks>
查看该任务17点的执行日志(task名字已修改)
zgrep -e '2016-10-28 17:' channel-task.log.2016-10-28.log.gz | grep -e 'MyTask2'
2016-10-28 17:14:25,002 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] start task >> .MyTask2@186d315
2016-10-28 17:14:35,980 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] complete task MyTask2@186d315
2016-10-28 17:14:40,002 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] start task >> .MyTask2@186d315
2016-10-28 17:14:50,681 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] complete task .MyTask2@186d315
2016-10-28 17:14:55,003 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] start task >> .MyTask2@186d315
2016-10-28 17:15:05,613 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] complete task MyTask2@186d315
2016-10-28 17:20:35,246 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] start task >> .MyTask2@186d315
2016-10-28 17:20:46,051 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] complete task .MyTask2@186d315
2016-10-28 17:20:50,003 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] start task >> .MyTask2@186d315
2016-10-28 17:21:00,974 INFO [pool-8-thread-1 - ] task.AbstractTask - [TASK] complete task MyTask2@186d315
MyTask2每10秒钟执行一次,但是在17:15:05 到17:20:35之间,5分钟内定时任务没有执行。
执行命令 zgrep -e '2016-10-28 17:1' task.log.2016-10-28.log.gz
(当天17点10几分发生的日志)。然后在查询日志发现,这5分钟之内有大量的日志在执行。
2016-10-28 17:17:20,202 INFO [pool-8-thread-1 - ] task.MyTask3 - compare query order[ 211621610280893418 ]
2016-10-28 17:17:20,477 INFO [pool-8-thread-1 - ] task.MyTask3 - compare query order[ 211621610280893401 ]
2016-10-28 17:17:20,731 INFO [pool-8-thread-1 - ] task.MyTask3 - compare query order[ 211621610280893402 ]
.........中间省略n条日志
2016-10-28 17:19:59,752 INFO [pool-8-thread-1 - ] task.MyTask3 - compare query order[ 211621610280894049 ]
通过过以上日志可以看出,该线程[pool-8-thread-1 - ]
一直在处理MyTask3任务,此时断定 task:scheduled 配置默认是单线程串行的。网上查找资料发现如下配置可以解决问题:
<task:scheduler id="scheduler" pool-size="3" />
<task:scheduled-tasks scheduler="scheduler" >
<task:scheduled ref="myTask1" method="run" cron="0 0/59 11-23 * * ?"/>
<task:scheduled ref="myTask2" method="run" cron="0/10 * * * * ?"/>
<task:scheduled ref="myTask3" method="run" cron="0/10 * * * * ?"/>
</task:scheduled-tasks>
MethodInvokingJobDetailFactoryBean并发问题
Spring中可以通过配置方便的实现周期性定时任务管理,这需要用到以下几个类:
- MethodInvokingJobDetailFactoryBean:此工厂主要用来制作一个jobDetail,即制作一个任务。由于我们所做的定时任务根本上讲其实就是执行一个方法。所以用这个工厂比较方便。
- SimpleTriggerBean:定时器,负责配置启动时间、执行周期。
- SchedulerFactoryBean:触发器,负责配置所有定时器。
<?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:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<bean id="myJob1Obj" class="com.vip.service.job.MyJob1" />
<bean id="myJob1"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myJob1Obj" />
<property name="targetMethod" value="doJob" />
<property name="concurrent" value="false" />
</bean>
<bean id="myJob1CronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJob1" />
<property name="cronExpression" value="0/3 * * * * ?"></property>
</bean>
<bean id="myJob2Obj" class="com.vip.service.job.MyJob2" />
<bean id="myJob2" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myJob2Obj" />
<property name="targetMethod" value="doJob" />
<property name="concurrent" value="false" />
</bean>
<bean id="myJob2CronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJob2" />
<property name="cronExpression" value="0/5 * * * * ?"></property>
</bean>
<bean id="myJob3Obj" class="com.vip.service.job.MyJob3" />
<bean id="myJob3"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myJob3Obj" />
<property name="targetMethod" value="doJob" />
<property name="concurrent" value="false" />
</bean>
<bean id="myJob3CronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJob3" />
<property name="cronExpression" value="0/5 * * * * ?"></property>
</bean>
<bean id="jobQuertz"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
lazy-init="false">
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<property name="jobDetails">
<list>
<ref bean="myJob3" />
<ref bean="myJob1" />
<ref bean="myJob2" />
</list>
</property>
<property name="schedulerName" value="AdsScheduler" />
<property name="triggers">
<list>
<ref bean="myJob3CronTrigger" />
<ref bean="myJob2CronTrigger" />
<ref bean="myJob1CronTrigger" />
</list>
</property>
<property name="startupDelay" value="15" />
<property name="overwriteExistingJobs" value="true" />
<property name="autoStartup" value="true" />
</bean>
</beans>
concurrent:对于相同的JobDetail,当指定多个Trigger时, 很可能第一个job完成之前,第二个job就开始了。定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成之前开始。
MethodInvokingJobDetailFactoryBean类默认是并发执行的,这时候如果不设置“concurrent”为false,很可能带来并发或者死锁的问题,而且几率较小,不容易复现,请大家使用的时候注意设置“concurrent”。
网友评论