亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Scheduling本地任務(wù)調(diào)度設(shè)計(jì)與實(shí)現(xiàn)方式

 更新時(shí)間:2024年04月15日 09:19:31   作者:肥肥技術(shù)宅  
這篇文章主要介紹了Spring Scheduling本地任務(wù)調(diào)度設(shè)計(jì)與實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、Spring Boot 集成 Scheduling

對(duì)于不涉及分布式計(jì)算又關(guān)于時(shí)間的任務(wù)處理,也就是本地任務(wù)調(diào)度(Task Schduling)既是 Spring Framework 的集成功能,也是 Spring Boot 的重要特性。

在 Spring Boot 中,任務(wù)調(diào)度的使用得到了極大簡(jiǎn)化。

1、簡(jiǎn)單任務(wù)調(diào)度

從官方文檔介紹中,在任務(wù)調(diào)度類上聲明 EnableScheduling 和 @Configuration,并在調(diào)度方法添加 @Scheduled 就可以完成任務(wù)調(diào)度。

@Configuration
@EnableScheduling
public class SayHelloTask {
 
    @Scheduled(cron = "${hello.schedule.cron:*/10 * * * * *}")
    public void sayHello() {
        System.out.print("Hello,Schedule");
    }
}

@Scheduled 支持 cron 表達(dá)式,它的使用在這篇文章中做了介紹。

Spring Boot 做了增強(qiáng),可以添加默認(rèn)值,優(yōu)先從配置文件中讀取 hello.schedule.cron,如果為空則使用后面默認(rèn)的值,也就是每十秒鐘執(zhí)行一次 sayHello 方法。

線程池默認(rèn)使用一個(gè)線程,可使用 spring.task.scheduling 命名空間進(jìn)行如下微調(diào):

properties復(fù)制代碼spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2

2、自定義任務(wù)調(diào)度

如果需要擴(kuò)展任務(wù)調(diào)度,可以實(shí)現(xiàn) SchedulingConfigurer 來(lái)完成。

@Component
public class SayHiSchedulingConfigurer implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(4);
        threadPoolTaskScheduler.setThreadNamePrefix("helloschedules");
        threadPoolTaskScheduler.initialize();
 
        taskRegistrar.setScheduler(threadPoolTaskScheduler);
    }
}

這樣,調(diào)整了任務(wù)調(diào)度的線程池大小,也修改了線程日志名稱,便于日志分析定位。

二、Spring Scheduling 設(shè)計(jì)說(shuō)明

Spring Boot 對(duì) TaskExecution and Scheduling 做了簡(jiǎn)要使用說(shuō)明,深入了解本地任務(wù)調(diào)度可以參考 Spring Framework 對(duì) TaskExecution and Scheduling 的介紹。

下面就以 Spring Boot 3.1.2 以及 Spring Framework 6.0.11 版本為例,重點(diǎn)分析 Spring 對(duì)本地任務(wù)調(diào)度的實(shí)現(xiàn)方法。

首先,打開 @EnableScheduling 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
 
}

這個(gè)注解寫了大段的注釋用于解釋任務(wù)調(diào)度的用法,從注解中我們可以了解到本地調(diào)度的設(shè)計(jì)說(shuō)明,這是后面分析源碼、擴(kuò)展實(shí)現(xiàn)、最佳實(shí)踐的基礎(chǔ):

  • 簡(jiǎn)單的任務(wù)調(diào)度可以通過(guò)在方法上標(biāo)記 @Scheduled 實(shí)現(xiàn),Spring 提供了三種調(diào)度模式 cron、fixedRate 和 fixedDelay,也就是 cron 表達(dá)式執(zhí)行、固定頻率執(zhí)行與固定延遲執(zhí)行。
  • Spring 容器會(huì)先掃描 org.springframework.scheduling.TaskScheduler 和 java.util.concurrent.ScheduledExecutorService 兩個(gè) bean,如果都沒(méi)有注入的話會(huì)創(chuàng)建一個(gè)默認(rèn)的單線程任務(wù)調(diào)度器。
  • 如果 @Scheduled 無(wú)法滿足任務(wù)調(diào)度配置的話,或者需要運(yùn)行時(shí)配置 fixRate 與 fixDelay,可以通過(guò)標(biāo)記 @Configuration 來(lái)實(shí)現(xiàn) SchedulingConfigurer,這樣就可以訪問(wèn)底層的 ScheduledTaskRegistrar 實(shí)例,實(shí)現(xiàn)細(xì)粒度的任務(wù)控制。注意使用類似 @Bean(destroyMethod="shutdown") 來(lái)保證自定義任務(wù)執(zhí)行器在 Spring 應(yīng)用程序上下文關(guān)閉時(shí)也能夠正確關(guān)閉。
  • 最重要的,EnableScheduling 僅適用于本地應(yīng)用程序上下文,也就是我們說(shuō)的本地任務(wù)調(diào)度。分布式任務(wù)調(diào)度會(huì)有其他解決方案。

三、Spring Scheduling 關(guān)鍵類初探

1、SchedulingConfiguration

使用 @EnableScheduling 就會(huì)自動(dòng)引入這個(gè)注解,它只做了一件事就是注入了一個(gè) bean ScheduledAnnotationBeanPostProcessor。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
 
    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
 
}

2、ScheduledAnnotationBeanPostProcessor

ScheduledAnnotationBeanPostProcessor 是任務(wù)調(diào)度的核心類,由 @EnableScheduling 注解自動(dòng)注冊(cè),作為 bean 的后置處理器實(shí)現(xiàn)了大量接口。核心功能是提供 cron、fixedRate 和 fixedDelay 三種調(diào)度模式,識(shí)別 @Scheduled 標(biāo)記的方法,并轉(zhuǎn)換成 TaskScheduler 可調(diào)度的任務(wù)。以及,識(shí)別所有 SchedulingConfigurer 的實(shí)例,允許自定義使用調(diào)度任務(wù)或?qū)θ蝿?wù)進(jìn)行細(xì)粒度的控制。

(1)postProcessAfterInitialization

忽略掉細(xì)枝末節(jié),方法的核心功能是掃描帶有 @Scheduled 注解的方法,并處理成標(biāo)準(zhǔn)化任務(wù)。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    // ...
    AnnotationUtils.isCandidateClass(targetClass, List.of(Scheduled.class, Schedules.class))) {
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
    (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
        Set<Scheduled> scheduledAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                method, Scheduled.class, Schedules.class);
        return (!scheduledAnnotations.isEmpty() ? scheduledAnnotations : null);
    });
    
    // ...
    annotatedMethods.forEach((method, scheduledAnnotations) ->
            scheduledAnnotations.forEach(scheduled -> processScheduled(scheduled, method, bean)));
 
    // ...
    return bean;
}

(2)processScheduled

將掃描到的所有調(diào)度配置借助 ScheduledTaskRegistrar 轉(zhuǎn)換成標(biāo)準(zhǔn)化調(diào)度任務(wù),直接被異步執(zhí)行,返回結(jié)果交給 ScheduledTask.future。

protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
    try {
        Runnable runnable = createRunnable(bean, method);
        boolean processedSchedule = false;
        String errorMessage =
                "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
 
        Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
 
        // ...
 
        // Check cron expression
        String cron = scheduled.cron();
        if (StringUtils.hasText(cron)) {
            String zone = scheduled.zone();
            if (this.embeddedValueResolver != null) {
                cron = this.embeddedValueResolver.resolveStringValue(cron);
                zone = this.embeddedValueResolver.resolveStringValue(zone);
            }
            if (StringUtils.hasLength(cron)) {
                Assert.isTrue(initialDelay.isNegative(), "'initialDelay' not supported for cron triggers");
                processedSchedule = true;
                if (!Scheduled.CRON_DISABLED.equals(cron)) {
                    TimeZone timeZone;
                    if (StringUtils.hasText(zone)) {
                        timeZone = StringUtils.parseTimeZoneString(zone);
                    }
                    else {
                        timeZone = TimeZone.getDefault();
                    }
                    tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
                }
            }
        }
 
        // ...
 
        // Finally register the scheduled tasks
        synchronized (this.scheduledTasks) {
            Set<ScheduledTask> regTasks = this.scheduledTasks.computeIfAbsent(bean, key -> new LinkedHashSet<>(4));
            regTasks.addAll(tasks);
        }
 
        // ...
}

這里分析一下 fixedDelay 與 fixedRate 的區(qū)別:

fixedDelay 屬性可確保在執(zhí)行任務(wù)的結(jié)束時(shí)間與下一次執(zhí)行任務(wù)的開始時(shí)間之間有 n 毫秒的延遲。當(dāng)我們需要確保只有一個(gè)任務(wù)實(shí)例一直在運(yùn)行時(shí),該屬性特別有用。

而 fixedRate 屬性是每 n 毫秒運(yùn)行一次計(jì)劃任務(wù)。它不會(huì)檢查任務(wù)之前的執(zhí)行情況。如果任務(wù)的所有執(zhí)行都是獨(dú)立的,這一點(diǎn)就很有用。如果我們不希望超出內(nèi)存和線程池的大小,那么 fixedRate 就會(huì)非常方便。不過(guò),如果進(jìn)入的任務(wù)不能快速完成,就有可能出現(xiàn)內(nèi)存不足異常。

(3)finishRegistration

這個(gè)方法完成任務(wù)調(diào)度器注冊(cè)。提供自定義調(diào)度任務(wù)擴(kuò)展點(diǎn)的 SchedulingConfigurer 接口,并被全部掃描進(jìn)來(lái)。

最后,查找 TaskScheduler,添加到 ScheduledTaskRegistrar 中。

private void finishRegistration() {
    if (this.scheduler != null) {
        this.registrar.setScheduler(this.scheduler);
    }
 
    if (this.beanFactory instanceof ListableBeanFactory lbf) {
        Map<String, SchedulingConfigurer> beans = lbf.getBeansOfType(SchedulingConfigurer.class);
        List<SchedulingConfigurer> configurers = new ArrayList<>(beans.values());
        AnnotationAwareOrderComparator.sort(configurers);
        for (SchedulingConfigurer configurer : configurers) {
            configurer.configureTasks(this.registrar);
        }
    }
    // ...
 
    this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, true /false));
    
    // ...
 
    this.registrar.afterPropertiesSet();
}

3、ScheduledTaskRegistrar

ScheduledAnnotationBeanPostProcessor 被 SchedulingConfiguration 創(chuàng)建的時(shí)候二話沒(méi)說(shuō),初始化就創(chuàng)建了一個(gè) ScheduledTaskRegistrar。

ScheduledTaskRegistrar 用于輔助任務(wù)注冊(cè)到 TaskScheduler 中,尤其是結(jié)合 @EnableAsync 注解和 SchedulingConfigurer 的回調(diào)方法時(shí)。這個(gè) bean 在創(chuàng)建時(shí)會(huì)先判斷是否存在 TaskScheduler,沒(méi)有就會(huì)創(chuàng)建一個(gè)單線程任務(wù)調(diào)度池,然后逐一把調(diào)度任務(wù)添加到任務(wù)隊(duì)列中。

(1)scheduleTasks

在被 ScheduledAnnotationBeanPostProcessor#finishRegistration 調(diào)用時(shí),就是完成任務(wù)標(biāo)準(zhǔn)化,afterPropertiesSet 只完成了一個(gè)方法完成,也就是 scheduleTasks。

protected void scheduleTasks() {
    if (this.taskScheduler == null) {
       this.localExecutor = Executors.newSingleThreadScheduledExecutor();
       this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    }
    
    // ...
    
    if (this.cronTasks != null) {
       for (CronTask task : this.cronTasks) {
          addScheduledTask(scheduleCronTask(task));
       }
    }
    
    // ...
}

(2)scheduleCronTask

使用適配器模式將各個(gè) Task 轉(zhuǎn)換成 ScheduledTask,成為標(biāo)準(zhǔn)化任務(wù)執(zhí)行。

其他幾個(gè)方法 scheduleTriggerTask,scheduleFixedRateTask,scheduleFixedDelayTask 都是類似的。

public ScheduledTask scheduleCronTask(CronTask task) {
    ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
    boolean newTask = false;
    if (scheduledTask == null) {
       scheduledTask = new ScheduledTask(task);
       newTask = true;
    }
    if (this.taskScheduler != null) {
       scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
    }
    else {
       addCronTask(task);
       this.unresolvedTasks.put(task, scheduledTask);
    }
    return (newTask ? scheduledTask : null);
}

四、Spring Scheduling 調(diào)用時(shí)序

@EnableScheduling 的引入使得 Spring 容器開啟了對(duì)本地調(diào)度任務(wù)掃描,并自動(dòng)裝配了 SchedulingConfiguration,實(shí)際上是引入了 ScheduledAnnotationBeanPostProcessor。

ScheduledAnnotationBeanPostProcessor 實(shí)現(xiàn)了 postProcessAfterInitialization 首先被執(zhí)行,在 bean 初始化完成后對(duì) @Scheduled 注解進(jìn)行掃描并轉(zhuǎn)換成標(biāo)準(zhǔn)化本地調(diào)度任務(wù) ScheduledTask。

任務(wù)轉(zhuǎn)換完成后就會(huì)異步執(zhí)行任務(wù),但是需要等待主線程對(duì)線程池的初始化。ScheduledAnnotationBeanPostProcessor 實(shí)現(xiàn)了 onApplicationEvent 完成對(duì)任務(wù)注冊(cè)的初始化,包括自定義調(diào)度任務(wù)配置

SchedulingConfigurer 的所有實(shí)現(xiàn)的掃描,以及對(duì)調(diào)度任務(wù)執(zhí)行者 TaskScheduler 的初始化,如果都沒(méi)有注入的話會(huì)創(chuàng)建一個(gè)默認(rèn)的單線程任務(wù)調(diào)度器。

最后,準(zhǔn)備工作完成后,由 ScheduledTaskRegistrar 的各類型調(diào)度任務(wù)分別調(diào)用 ScheduledFuture 的 schedule 方法完成任務(wù)。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論