当前位置:首页 > 科技  > 软件

Spring Boot 定时调度任务高级篇:调度任务的实现原理

来源: 责编: 时间:2023-12-26 09:30:17 465观看
导读前言源码的世界是一片汪洋大海,springboot的源码更是如此,虽然用的时候似乎很简单,然而正是因为其内部的设计巧妙、复杂,才造就了其使用上的简单易上手。罗马不是一天建起来的,要完全理解它也并非一时的事,所以这里给大家分

前言

源码的世界是一片汪洋大海,springboot的源码更是如此,虽然用的时候似乎很简单,然而正是因为其内部的设计巧妙、复杂,才造就了其使用上的简单易上手。罗马不是一天建起来的,要完全理解它也并非一时的事,所以这里给大家分享一些我自己阅读源码时的一些体会,那就是不要因为一时看不懂而着急或放弃,慢慢来,一点一点来,早晚能弄明白,另外一点就是,带着问题去看,时刻要把握好自己的问题是什么,不要在源码中迷失了自己。Tm028资讯网——每日最新资讯28at.com

核心问题

关于Springboot调度任务的工作原理,实际就是两个问题:Tm028资讯网——每日最新资讯28at.com

第一个问题,调度任务是如何被注册的?Tm028资讯网——每日最新资讯28at.com

第二个问题,注册的调度任务是如何触发执行的?Tm028资讯网——每日最新资讯28at.com

实现方法

关于Springboot调度任务的具体实现方法已经在上一篇文章中详细介绍过,这里再作一下简单的梳理、归纳,主要两种方法:Tm028资讯网——每日最新资讯28at.com

  • 基于注解@Scheduled
  • 基于接口SchedulingConfigurer

两种方法都需要使用@EnableScheduling(第一个核心关键类)来开启调度任务功能。Tm028资讯网——每日最新资讯28at.com

基于注解@Scheduled

基于注解@Scheduled内的属性,可以分为三类调度任务:Tm028资讯网——每日最新资讯28at.com

  1. cron表达式可以通过若干数字、空格、符号按一定的规则,组成一组字符串,定义调度任务的执行规则;
  2. fixedDelay以每次调度任务执行完成后间隔指定时间再开始下一次的调度任务,单位是毫秒;
  3. fixedRate以每次调度任务开始的时间间隔指定时间再开始下一次的调度任务,单位是毫秒;

基于接口SchedulingConfigurer

实现SchedulingConfigurer接口,并重写configureTasks()方法,在重写configureTasks()里,完成调度任务的注册;Tm028资讯网——每日最新资讯28at.com

工作原理

基于注解@Scheduled和基于接口SchedulingConfigurer接口,都需要使用@EnableScheduling来开启调度任务注册功能。进入@EnableScheduling注解内部观察一番,发现通过@Import引入了一个配置类SchedulingConfiguration.class(第二个核心关键类)Tm028资讯网——每日最新资讯28at.com

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

顺着SchedulingConfiguration.class进入其内部,又发现了一个大秘密:ScheduledAnnotationBeanPostProcessor(第三个核心关键类)Tm028资讯网——每日最新资讯28at.com

@Configuration@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();   }}

对Spring生命周期比较熟悉的话,一看到XxxxBeanPostProcessor,就能想到postProcessAfterInitialization()方法了。Tm028资讯网——每日最新资讯28at.com

  • BeanPostProcessor,spring的后置处理器,重要的扩展点之一,可以在在Bean对象初始化前后回调BeanPostProcessor中定义的两个方法:
  • postProcessBeforeInitialization()方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization()方法会在每个bean对象的初始化方法调用之后被回调

基于注解@Scheduled与基于接口SchedulingConfigurer调度任务实现入口是一样的,其具体实现是不一样的。Tm028资讯网——每日最新资讯28at.com

基于注解@ScheduledTm028资讯网——每日最新资讯28at.com

还记得第一个问题是什么吗?(调度任务是如何被注册的?)基于注解@Scheduled调度任务的注册就是在中实现在ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization()方法中实现的,具体的步骤是:Tm028资讯网——每日最新资讯28at.com

在spring容器中找出被注解@Scheduled.class,@Schedules.class标记过的方法Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

接着遍历这些方法,而实际的调度任务注册逻辑也是从这里(processScheduled()方法)开始的Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

在实现方法里已经梳理清楚了,springboot的调度任务实际上可以分为三类,在本文中就以常用cron表达式类为例来说明其注册、执行过程。进入processScheduled()内,首先把@Scheduled标记的方法包装成一个Runnable任务(实现java多线程的方法之一就是实现java.lang.Runnable接口)Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

而在processScheduled()也会根据不同类别的任务分别作处理,这里以cron表达式类的调度任务为例看一下后续是怎么处理的。Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

进入thsi.registrat.scheduleCronTask()方法内部(第五个核心关键类ScheduledTaskRegistrar),很多人认为下面就是触发开始执行调度任务的执行了;实际上这么认为是错的,因为这个时候Spring的容器还未启动完成,任务的调度器(this.taskScheduleer是null)还未实例化,所以这里只是完成调度任务的注册。Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

分析源码就是这样,得慢慢来,不要急,要牢牢把握住自己的问题,千万不要迷路了。下面开始分析第二个问题:注册的调度任务是如何触发执行的。Tm028资讯网——每日最新资讯28at.com

任务注册上面说到了ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization()方法被触发执行,一直到执行到thsi.registrat.scheduleCronTask()只是完成了调度任务的注册,并没有开始执行,实际上注册完成的调度任务开始执行是在Spring容器启动完成后,会发布一个启动完成的事件(ContextRefreshedEvent),ScheduledAnnotationBeanPostProcessor实现了Spring的监听器接口(ApplicationListener),因此实际触发已注册调度任务的执行的入口是在监听方法中(ScheduledAnnotationBeanPostProcessor#onApplicationEvent);Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

在finishRegistration()中,分别做了哪些事呢?Tm028资讯网——每日最新资讯28at.com

第一,调用ScheduledAnnotationBeanPostProcessor#resolveSchedulerBean()查找任务调度器(TaskScheduler);Tm028资讯网——每日最新资讯28at.com

第二,实际找到了ThreadPoolTaskScheduler作为实际的任务调度器,然后调用ScheduledTaskRegistrar#setTaskScheduler()完成任务调度器的配置;Tm028资讯网——每日最新资讯28at.com

第三,接着调用ScheduledTaskRegistrar#afterPropertiesSet()开始实际的任务触发执行;不同类型的调度任务是在ScheduledTaskRegistrar#scheduleTasks()中完成判断,然后分别调用各自的方法执行的;以cron表达式类型的调度任务为例,实际上最后由ScheduledTaskRegistrar#scheduleCronTask()实际完成。Tm028资讯网——每日最新资讯28at.com

至此,基于注解@Scheduled的调度任务实现原理基本分析完了,下面是我就调度任务的注册和执行两个时机为入口,绘制了整个过程的一个调用时序图,供大家参考学习:Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

基于接口SchedulingConfigurer

基于接口SchedulingConfigurer的Springboot调度任务,与基于注解不同,其调度任务的注册、执行都是在Spring容器启动完成以后,发布ContextRefreshedEvent事件后,实现了Srping事件监听器的接口(ApplicationListener)的ScheduledAnnotationBeanPostProcessor类的onApplicationEvent()被触发,然后才开始调度任务的注册和执行,下面具体分析一下:Tm028资讯网——每日最新资讯28at.com

第一步,查找所有SchedulingConfigurer接口的实现类,然后遍历所有实现类并执行SchedulingConfigurer#configureTasks,就这么朴实无华,完成了所有通过实现SchedulingConfigurer接口(第四个核心关键类)的调度任务注册;(第一个问题:调度任务是如何被注册的,到这已经有答案了)Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

第二步,从ScheduledTaskRegistrar#afterPropertiesSet()进入开始调度任务的触发执行阶段(第二个问题,注册的的调度任务是如何被执行的),afterPropertiesSet()中实际是调用了ScheduledTaskRegistrar#scheduleTasks()方法;Tm028资讯网——每日最新资讯28at.com

如果在实现SchedulingConfigurer接口,重写configureTasks(),没有显性的指定任务调度器(TaskScheduler),在scheduleTasks()里,会初始化一个默认的任务调度器,这里要注意,默认的使用的是单线程的线程池;Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

接下来就是根据实际注册的调度任务类型分别开始调度任务的实际执行了,在上一篇文章中,我注册的是TriggerTasks类型的任务,所以这里就会调用ScheduledTaskRegistrar#scheduleTriggerTask()方法开始调度任务的执行。Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

至此,基于接口SchedulingConfigurer的Springboot调度任务的工作任务也基本分析完了,下面是整个过程的调用时序图,大家可以参考一下:Tm028资讯网——每日最新资讯28at.com

图片图片Tm028资讯网——每日最新资讯28at.com

核心类回顾Tm028资讯网——每日最新资讯28at.com

  • @EnableScheduling,开启Springboot任务调度功能的标识注解;
  • SchedulingConfiguration,Springboot任务调度功能的自动配置类,作用是实例化ScheduledAnnotationBeanPostProcessor;
  • ScheduledAnnotationBeanPostProcessor,调用任务的注册、执行的触发入口;
  • SchedulingConfigurer,调度任务的扩展接口,允许用户自定义调度任务的注册;
  • ScheduledTaskRegistrar,调度任务注册中心,调用任务的实际管理者;

总结

通过分析Springboot两种调度任务的实现方法的工作原理,有什么收获呢?Tm028资讯网——每日最新资讯28at.com

第一,默认情况下,使用单线程的线程池来执行调度任务,性能上不会太高,适用场景有限;Tm028资讯网——每日最新资讯28at.com

第二,即便显性的任务调度器配置了拥用较多线程的线程池,与现有其他业务同处一个工程,也会挤占其他业务的服务器资源;Tm028资讯网——每日最新资讯28at.com

所以,在实际使用过程中,应根据实际场景和资源配置进行选择。Tm028资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-54165-0.htmlSpring Boot 定时调度任务高级篇:调度任务的实现原理

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 使用React微前端的完整指南

下一篇: Java中的序列化和反序列化它们的作用和用途是什么?

标签:
  • 热门焦点
  • 小米平板5 Pro 12.4简评:多专多能 兼顾影音娱乐的大屏利器

    疫情带来了网课,网课盘活了安卓平板,安卓平板市场虽然中途停滞了几年,但好的一点就是停滞的这几年行业又有了新的发展方向,例如超窄边框、高刷新率、多摄镜头组合等,这就让安卓
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 企业采用CRM系统的11个好处

    客户关系管理(CRM)软件可以为企业提供很多的好处,从客户保留到提高生产力。  CRM软件用于企业收集客户互动,以改善客户体验和满意度。  CRM软件市场规模如今超过580
  • 量化指标是与非:挽救被量化指标扼杀的技术团队

    作者 | 刘新翠整理 | 徐杰承本文整理自快狗打车技术总监刘新翠在WOT2023大会上的主题分享,更多精彩内容及现场PPT,请关注51CTO技术栈公众号,发消息【WOT2023PPT】即可直接领取
  • 谷歌KDD'23工作:如何提升推荐系统Ranking模型训练稳定性

    谷歌在KDD 2023发表了一篇工作,探索了推荐系统ranking模型的训练稳定性问题,分析了造成训练稳定性存在问题的潜在原因,以及现有的一些提升模型稳定性方法的不足,并提出了一种新
  • 从零到英雄:高并发与性能优化的神奇之旅

    作者 | 波哥审校 | 重楼作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得
  • 零售大模型“干中学”,攀爬数字化珠峰

    文/侯煜编辑/cc来源/华尔街科技眼对于绝大多数登山爱好者而言,攀爬珠穆朗玛峰可谓终极目标。攀登珠峰的商业路线有两条,一是尼泊尔境内的南坡路线,一是中国境内的北坡路线。相
  • 雅柏威士忌多款单品价格大跌,泥煤顶流也不香了?

    来源 | 烈酒商业观察编 | 肖海林今年以来,威士忌市场开始出现了降温迹象,越来越多不断暴涨的网红威士忌也开始悄然回归市场理性。近日,LVMH集团旗下苏格兰威士忌品牌雅柏(Ardbeg
  • 微软发布Windows 11新版 引入全新任务栏状态

    近日,微软发布了Windows 11新版,而Build 22563更新主要引入了几周前曝光的平板模式任务栏等,系统更流畅了。更新中,Windows 11加入了专门针对平板优化的任务栏
Top