spring定时器任务多任务串行执行问题排查

时间:2021-09-05 23:24:15

原文:https://www.cnblogs.com/youtianhong/p/6027249.html

 

    最近发现个生产问题,定时器任务某些任务没有及时执行。经过研究排查发现spring 定时器任务scheduled-tasks默认配置是单线程串行执行的,这就造成了若某个任务执行时间过长,其他任务一直在排队,业务逻辑没有及时处理的问题。

如下是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>

 

如果是注解的方式

@Scheduled(cron = "0 0/1 * * * ?")

那就在applicationContext.xml文件加入<task:scheduler id="scheduler" pool-size="2" />这一行,pool-size表示线程的个数

 

 

 

或者是下面的这一种方式

 

    1.简单单任务定时器的spring配置

<!-- 配置定时任务,用于初始化定时器 -->  
    <bean id="InitJobDetail"  
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject">  
            <ref bean="ReportJobTodo"/>  
        </property>  
        <property name="targetMethod">  
            <value>initJobTrigger</value>  
        </property>  
        <property name="concurrent" value ="false"/>    
    </bean>  
    <bean id="ReportJobTodo"  
        class="cn.com.gsoft.report.timetask.ReportJobTodo">  
    </bean>  
    <bean id="InitTrigger"  
        class="org.springframework.scheduling.quartz.CronTriggerBean">  
        <property name="jobDetail">  
            <ref bean="InitJobDetail"/>  
        </property>  
        <property name="cronExpression">  
            <value>* * * * * ?</value>  
        </property>  
    </bean>  
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
            <list>  
                <ref local="InitTrigger"/>  
            </list>  
        </property>  
    </bean>  
<!-- 配置定时任务,用于初始化定时器 -->
 <bean id="InitJobDetail"
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject">
   <ref bean="ReportJobTodo"/>
  </property>
  <property name="targetMethod">
   <value>initJobTrigger</value>
  </property>
     <property name="concurrent" value ="false"/> 
 </bean>
 <bean id="ReportJobTodo"
  class="cn.com.gsoft.report.timetask.ReportJobTodo">
 </bean>
 <bean id="InitTrigger"
  class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">
   <ref bean="InitJobDetail"/>
  </property>
  <property name="cronExpression">
   <value>* * * * * ?</value>
  </property>
 </bean>
 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
   <list>
    <ref local="InitTrigger"/>
   </list>
  </property>
 </bean>

 

说明:(1).InitJobDetail实例声明了需要执行的任务。其中targetObject说明了需要执行的方法所在的实例对象,targetMethod说明了要执行的方法,concurrent用于说明多个任务是否同步执行。

         (2).InitTrigger声明了一个触发器。jobDetail属性指明需要执行的任务,cronExpression声明了该任务在什么时候执行,该表达式跟linux下的crontab定时程序中使用的表达式是一样的,具体使用方法可以参考文后的参考资料。

         (3).SchedulerFactoryBean中可以定义多个触发器,以实现多任务。