為什么我的定時(shí)器不跑了

最近要搞個(gè)小服務(wù)運(yùn)行在多家客戶的windows服務(wù)器上,里面有兩個(gè)定時(shí)任務(wù),一個(gè)是定時(shí)檢查版本號(hào),一個(gè)是定時(shí)向服務(wù)器匯報(bào)狀態(tài),都使用 spring 的@Scheduled實(shí)現(xiàn)。
昨天晚上讓它們跑著,今天上午一看,居然沒有匯報(bào)狀態(tài)了,(無奈,肯定有bug)。

登錄服務(wù)器,看到服務(wù)還在運(yùn)行,看了一下沒有打日志了,訪問端口有數(shù)據(jù)返回,那這個(gè)服務(wù)應(yīng)該還活著,定時(shí)器不跑了?
沒有太多的辦法,老實(shí)下載了一個(gè)jdk安裝, jstack看一下,發(fā)現(xiàn)定時(shí)任務(wù)線程被阻塞了,而且居然是被RestTemplate阻塞的,想了一下,這個(gè)阻塞的原理可能比較復(fù)雜,暫不追究。
解決辦法挺簡單,老實(shí)地去設(shè)置了超時(shí)時(shí)間,想必下次不會(huì)阻塞這么久了。
等等,為什么我兩個(gè)定時(shí)器,一個(gè)線程阻塞了,還有一個(gè)去哪兒了? 這個(gè)不會(huì)是單線程吧?!
找到日志看一下,確實(shí)只有一個(gè)叫schedule-1的線程在跑,==!
有點(diǎn)超出我的想像,需要來看下源碼壓壓驚。
分析Spring的定時(shí)任務(wù)框架為什么線程是1, 從文檔入手,很好,在文檔中看到了配置類:


在這里插入圖片描述

通過SchedulingConfiguration的初始化,創(chuàng)建了ScheduledAnnotationBeanPostProcessor來掃描代碼創(chuàng)建定時(shí)任務(wù)。
查看初始過程,發(fā)現(xiàn)創(chuàng)建TaskScheduler的創(chuàng)建過程:

// Search for TaskScheduler bean...
this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false));

beanFactory是DefaultListableBeanFactory, 查看resolveSchedulerBean中代碼:

    private <T> T resolveSchedulerBean(BeanFactory beanFactory, Class<T> schedulerType, boolean byName) {
        if (byName) {
            //...
            return scheduler;
        }
        else if (beanFactory instanceof AutowireCapableBeanFactory) {
            NamedBeanHolder<T> holder = ((AutowireCapableBeanFactory) beanFactory).resolveNamedBean(schedulerType);
            if (this.beanName != null && beanFactory instanceof ConfigurableBeanFactory) {
                ((ConfigurableBeanFactory) beanFactory).registerDependentBean(holder.getBeanName(), this.beanName);
            }
            return holder.getBeanInstance();
        }
        else {
            return beanFactory.getBean(schedulerType);
        }
    }

這段是用beanFactory找到TaskScheduler.class的bean,并注冊依賴關(guān)系。
好吧,找下哪里配置了TaskScheduler,然后發(fā)現(xiàn)了TaskSchedulingAutoConfiguration:

    @Bean
    @ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class,
            ScheduledExecutorService.class })
    public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
        return builder.build();
    }

    @Bean
    @ConditionalOnMissingBean
    public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties,
            ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
        TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
        builder = builder.poolSize(properties.getPool().getSize());
        builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
        builder = builder.customizers(taskSchedulerCustomizers);
        return builder;
    }

可以看到這里設(shè)置了poolSize, 配置來自TaskSchedulingProperties, 激動(dòng)人心的時(shí)候到了,來看下TaskSchedulingProperties:

@ConfigurationProperties("spring.task.scheduling")
public class TaskSchedulingProperties {
//...
    public static class Pool {
        /**
         * Maximum allowed number of threads.
         */
        private int size = 1;
        public int getSize() {
            return this.size;
        }
        public void setSize(int size) {
            this.size = size;
        }
    }
}

好吧,除了http阻塞的問題,一切都明白了。
spring boot 默認(rèn)開啟單線程的定時(shí)任務(wù)執(zhí)行器, 然后某個(gè)定時(shí)器因?yàn)閔ttp阻塞被阻塞了,所有定時(shí)器都不跑了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容