Spring定時任務無故停止又不報錯的解決

Spring定時任務無故停止又不報錯

一開始是使用Spring自帶的定時器來配置定時任務的,簡單快捷,配置如下:

<bean id="refreshCache" class="com.gionee.baserom.search.job.RefreshCache" />
<task:scheduled-tasks>
    <task:scheduled ref="refreshCache" method="execute" cron="0 */30 * * * ?"/>
</task:scheduled-tasks>

但是使用一段時間之後就無故停止,且不報錯,所以沒有相關錯誤日志,需要重啟Tomcat之後才能繼續執行定時任務。

開始以為由於數據庫最大連接數的限制,設置成翻倍瞭之後仍出現這問題。在同學的提醒下意識到可能是線程阻塞導致,於是網上查到原因:

Spring定時任務默認都是並發執行的,不會等待上一次任務執行完畢,隻要間隔時間到就會執行。

解決方案

1.將JobDetail的concurrent屬性配置為false。不允許任務並發執行。

2.任務執行時間較長時,查找根本問題。

於是把Spring自帶的定時器改用Quartz,依賴相關包:

<dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.1</version>
</dependency>

定時任務配置如下:

<!-- 工作的bean -->
    <bean id="myJob" class=" com.gionee.baserom.exchangerate.job.DailyTaskJob" />
    <!-- 定義任務,為瞭避免線程阻塞,用concurrent=false -->
    <bean id="myJobDetail"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="myJob" />
        <property name="targetMethod" value="execute" />
        <property name="concurrent" value="false" />
    </bean>
    <!-- 配置觸發器  -->
    <bean id="myJobTrigger"
        class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="myJobDetail" />
        <property name="cronExpression" value="0 0/30 * * * ?" />
    </bean>
    <!-- 配置調度器 -->
    <bean name="startQuertz" lazy-init="false" autowire="no" destroy-method="destroy" 
        class="com.gionee.baserom.exchangerate.util.SchedulerFactoryBeanWithShutdownDelay" >
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.threadCount">1</prop>
            </props>
        </property>
        <property name="waitForJobsToCompleteOnShutdown">
            <value>false</value>
        </property>
        <property name="triggers">
            <list>
                <ref bean="myJobTrigger" />
            </list>
        </property>
    </bean>

在startQuartz中用到SchedulerFactoryBeanWithShutdownDelay是因為當Tomcat被關閉時,有可能導致任務線程並未完全關閉,導致內存泄漏。

SchedulerFactoryBeanWithShutdownDelay.java

import org.quartz.SchedulerException;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
public class SchedulerFactoryBeanWithShutdownDelay extends SchedulerFactoryBean {
    @Override  
    public void destroy() throws SchedulerException {  
        super.destroy();  
        try {  
            Thread.sleep(1000);  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
    }  
}

Spring定時任務跑完不再啟動

spring的定時任務有以下兩個特性

1、單定時任務之間是串行,之前的任務沒執行完,下一個任務不會啟動。

2、多個任務之間會相互幹擾,其他同一時刻啟動的任務沒執行完,下一個任務不會啟動。

排查方式

1、首先檢查自己的代碼,是否有死鎖、卡住、bug、http請求沒有設置超時時間等問題。

2、檢查是否所有定時任務都不啟動,如果是基本判斷是特性2導致的,檢查是哪個定時任務執行慢、卡住、出現bug等情況。

解決思路

1、修復bug,如果有的話。

2、如果就是有個任務執行慢,無法優化,可以不用spring的定時任務,改用Quartz。

依賴包

<dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.1</version>
</dependency>

配置:

<!-- 工作的bean -->
    <bean id="myJob" class=" com.gionee.baserom.exchangerate.job.DailyTaskJob" />
 
    <!-- 定義任務,為瞭避免線程阻塞,用concurrent=false -->
    <bean id="myJobDetail"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="myJob" />
        <property name="targetMethod" value="execute" />
        <property name="concurrent" value="false" />
    </bean>
 
    <!-- 配置觸發器  -->
    <bean id="myJobTrigger"
        class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="myJobDetail" />
        <property name="cronExpression" value="0 0/30 * * * ?" />
    </bean>
 
    <!-- 配置調度器 -->
    <bean name="startQuertz" lazy-init="false" autowire="no" destroy-method="destroy" 
        class="com.gionee.baserom.exchangerate.util.SchedulerFactoryBeanWithShutdownDelay" >
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.threadCount">1</prop>
            </props>
        </property>
        <property name="waitForJobsToCompleteOnShutdown">
            <value>false</value>
        </property>
        <property name="triggers">
            <list>
                <ref bean="myJobTrigger" />
            </list>
        </property>
    </bean>

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: