美文网首页
Spring调度定时任务的方式

Spring调度定时任务的方式

作者: 不知名的蛋挞 | 来源:发表于2019-01-13 22:48 被阅读8次

    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”。

    相关文章

      网友评论

          本文标题:Spring调度定时任务的方式

          本文链接:https://www.haomeiwen.com/subject/ewxvrqtx.html