前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务)

玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务)

作者头像
一猿小讲
发布于 2022-02-25 01:50:15
发布于 2022-02-25 01:50:15
1.4K1141
代码可运行
举报
文章被收录于专栏:一猿小讲一猿小讲
运行总次数:141
代码可运行

研发说:API 请求量到底啥情况呀?统计发粗来(万一访问量一直激增,导致服务宕了,要扣我绩效滴)。

运维说:定期统计一下服务器内存、CPU占用率(万一出故障了,这个锅谁来背?)

业务说:记得把订单支付状态通知一下业务线(我很谨慎,不然都不知道钱支付出去了,妥妥避坑)。

产品说:把每天凌晨 2 点通知用户还款功能简单实现一下(功能很简单,上午实现,下午上线,怎么实现我不管)。

运营说:把每月的业务情况统计粗来(我要向上管理,向上汇报要用到)。

财务说:把账户日末余额统计统计,发个报表粗来(我要去谈费率,为公司节省成本,不然年底就没奖杯可拿啦)。

老板说:每月 15 号发工资,记得把发薪结果统计粗来(我看看到底还能再创(砍)多少辉(人)煌(头))。

很显然,如上需求大概率都需要定时任务来支撑。在日常项目研发中,定时任务可谓是必不可少的一环。本次主要借助 Spring Boot 来谈谈如何实现定时任务。

1. 静态定时任务

所谓静态定时任务是指应用跑起来后,任务的执行时间无法进行动态修改。实现起来也比较简单,只需通过 Spring Boot 内置注解 @Scheduled 来实现,默认是启动单线程来跑任务,可以通过配置线程池开启多线程,下面逐一学习一下。

1.1. 单线程定时任务

1.1.1 开启定时任务功能

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • @EnableScheduling:用来开启定时任务功能,可以检测 Spring 管理的 bean 上 @Scheduled 的注解,系统默认会自动启动一个线程,来调度执行定时任务。

1.1.2 创建任务类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class DownLoadTask {

    private static final Log logger = LogFactory.getLog(DownLoadTask.class);

    @Scheduled(cron = "0 0/5 * * * ?")
    public void justDoIt() {
        logger.info("开始下载银行对账文件");
        logger.info("银行对账文件下载完成,进行解密操作");
        logger.info("银行对账文件下载解密完成");
    }
}

@Scheduled:主要用来完成任务的配置,如执行时间、间隔时间、延迟时间等等,其中有如下配置格式,可以自行体验体验。

1.1.3 运行验证

实现了一个每 5 分钟去银行下载一个对账文件的任务,跑起来效果如下。

回头去看,SpringBoot 开启定时任务的确很简单,几行代码就轻松搞定,so easy~。

但是,疑问来了。

疑问:若同时开启两个任务,会存在什么效果呢?若分别下载 A、B 两家银行的对账文件,如何支持呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class DownLoadTask {

    private static final Log logger = LogFactory.getLog(DownLoadTask.class);

    @Scheduled(cron = "0/1 * * * * ?")
    public void justDoItA() {
        logger.info("开始下载银行 A 的对账文件");
        logger.info("银行 A 对账文件下载完成,进行解密操作");
        logger.info("银行 A 对账文件下载解密完成");
    }

    @Scheduled(cron = "0/1 * * * * ?")
    public void justDoItB() {
        logger.info("开始下载银行 B 的对账文件");
        logger.info("银行 B 对账文件下载完成,进行解密操作");
        logger.info("银行 B 对账文件下载解密完成");
    }
}

程序跑起来,效果如下。

很显然,一个线程先办完 A,然后办 B,等上一个事儿办完了才办下一个事儿,不支持多线程。若项目里有多个任务要并行执行,而 Spring Boot 默认单线程来执行任务的方案就差点意思了。

不过无妨,Spring Boot 有开启多线程的方案,接下来看看如何开启多线程来执行任务。

1.2. 多线程定时任务

1.2.1 自定义线程池

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
public class SchedulerConfig {

    @Bean(name = "bankThreadPool")
    public Executor bankExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数为 3
        executor.setCorePoolSize(3);
        // 最大线程数为10
        executor.setMaxPoolSize(10);
        // 任务队列的大小
        executor.setQueueCapacity(3);
        // 线程前缀名
        executor.setThreadNamePrefix("bankExecutor-");
        // 线程存活时间
        executor.setKeepAliveSeconds(30);
        // 初始化
        executor.initialize();
        return executor;
    }
}
  • @Bean(name = "bankThreadPool"):方法级别上的注解,用来定义实例化线程池,别名为 bankThreadPool。

1.2.2 开启异步执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
@EnableAsync
public class DownLoadTask {

    private static final Log logger = LogFactory.getLog(DownLoadTask.class);

    @Async("bankThreadPool")
    @Scheduled(cron = "0/1 * * * * ?")
    public void justDoIt() {
        logger.info("开始下载银行 A 的对账文件");
        logger.info("银行 A 对账文件下载完成,进行解密操作");
        logger.info("银行 A 对账文件下载解密完成");
    }

    @Async("bankThreadPool")
    @Scheduled(cron = "0/1 * * * * ?")
    public void justDoIt2() {
        logger.info("开始下载银行 B 的对账文件");
        logger.info("银行 B 对账文件下载完成,进行解密操作");
        logger.info("银行 B 对账文件下载解密完成");
    }
}
  • @EnableAsync:表示开启对异步任务的支持,就可以使用多线程了。
  • @Async:在方法上加入这个注解,异步执行方法。Spring 会从指定的线程池中获取新线程来执行方法,@Async("name") 会用指定 name 的线程池来处理。

1.2.3 运行验证

显而易见,线程池已生效,多线程执行任务,任务之间相对独立、互不影响。

此时,简单的几行配置代码,足矣满足下载银行对账文件等简易场景的定时任务。

但是,任务执行的时间放在代码里总有种不妥,若因为走了狗屎运想调整一下任务执行的时间,那岂不是要重新改代码,重新发布上线?

疑问来了:如何动态修改任务执行的时间,而无需重新发布重启服务呢?

莫急,继续往下瞅。

2. 动态定时任务

由于 Spring Boot 内置的 @Scheduled 注解无法动态修改任务执行的时间,而实现 SchedulingConfigurer 接口提供了动态修改任务执行时间的可能性。

另外要维护任务执行的时间配置方式有很多种,思想很重要,实现无所谓,则其一便可。

  • 可以放在配置文件里,然后判断文件的修改时间是否发生变化,若变化了则重新读取配置的时间值;
  • 可以放在 Redis 里,然后任务执行的时候获取 Redis 里缓存的定时任务时间值;
  • 可以放在数据库里,然后任务执行的时候根据任务名称获取库中维护的定时任务时间值。(本次采取这个方案)

2.1. 定义任务类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 动态定时任务实现步骤
 * 步骤1:定义定时任务 DownLoadTaskV3 类实现 SchedulingConfigurer 接口;
 * 步骤2:编写定时任务要执行的业务逻辑;
 * 步骤3:数据库中配置任务执行的具体时间规则,记住任务名称
 * 步骤4:根据任务名称从数据库获取 Cron 参数,设置任务触发器,触发任务执行。
 * (仅抛砖引玉,可作进一步的抽象)
 */
@Component
@EnableScheduling
public class DownLoadTaskV3 implements SchedulingConfigurer {

    private static final Log logger = LogFactory.getLog(DownLoadTaskV3.class);

    @Autowired
    private TaskInfoRepository taskInfoRepository;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                // 步骤2:编写定时任务要执行的业务逻辑(可以进一步抽象)。
                logger.info("V3-开始下载银行 C 的对账文件");
                logger.info("V3-银行 C 对账文件下载完成,进行解密操作");
                logger.info("V3-银行 C 对账文件下载解密完成");
            }
        };
        
        // 步骤 4:根据任务名称从数据库获取 Cron 参数,设置任务触发器,触发任务执行。
        Trigger trigger = new Trigger() {
            /**
             * 每一次任务触发,都会调用一次该方法
             * 然后重新获取下一次的执行时间
             */
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                // 方式一:执行时间硬编码
                //String cron = "0/1 * * * * ?";

                // 方式二:动态获取执行时间(从数据库、redis 等都可以做任务执行时间的存储管理,本次以数据库为例)
                TaskInfo taskInfo = new TaskInfo();
                // 数据库配置的任务名称,通过任务名称获取对应的任务执行时间
                taskInfo.setJobName("downLoadTaskV3");
                Optional<TaskInfo> taskInfoOptional = taskInfoRepository.findOne(Example.of(taskInfo));
                // 获取配置的任务执行时间 cron 表达式
                String cron = taskInfoOptional.get().getCron();
                CronTrigger trigger = new CronTrigger(cron);
                return trigger.nextExecutionTime(triggerContext);
            }
        };
        // 设置任务触发器,触发任务执行。
        taskRegistrar.addTriggerTask(task, trigger);
    }
}
  • ScheduledTaskRegistrar.addTriggerTask(Runnable task, Trigger trigger):参数 task 中定义执行业务逻辑,在 trigger中进行修改定时任务的执行时间。

2.2. 创建任务信息表

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `SC_TASK_INFO` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `cron` varchar(32) DEFAULT NULL COMMENT '定时执行',
  `job_name` varchar(256) DEFAULT NULL COMMENT '任务名称',
  `status` char(1) DEFAULT '0' COMMENT '任务开启状态 0-关闭 2-开启',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) COMMENT='定时任务表';

INSERT INTO `SC_TASK_INFO` VALUES ('1', '0/10 * * * * ?', 'downLoadTaskV3', '2', '2020-03-01 16:43:50', '2020-06-11 11:06:09');

本次只用到了表中的 cron(定时表达式)、job_name(任务名称)两个字段,其它字段后续集成 Quartz 才会用到,可先忽略。

2.3. 创建实体类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entity
@Table(name = "sc_task_info")
public class TaskInfo implements Serializable {
    @Id
    private Integer id;
    @Column
    private String cron;
    @Column
    private String jobName;
    @Column
    private String status;
    @Column
    private Date createTime;
    @Column
    private Date updateTime;
    
    // 提供 setter/getter 方法
}

2.4. 定义持久化接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface TaskInfoRepository extends JpaRepository<TaskInfo, Integer> {
}

2.5. 引入依赖以及相关配置

主要是完成从数据库查询指定任务名称对应的定时配置,实现方式会有很多种,不要局限于本文提及的 JPA,可参考历史分享《玩转 Spring Boot 集成篇(MyBatis、JPA、事务支持)》引入 JPA、数据库连接依赖以及 application.properties 完成数据库连接配置。

2.6. 运行验证

库中对于 downLoadTaskV3 任务默认配置的时间为每 10 秒执行一次。

控制台输出如下。

手动修改数据库,把任务执行的时间表达式修改为每 1 秒执行一次。

控制台输出效果如下,很显然已经生效了。

至此,定时任务的时间就可以动态修改生效了,若再实现一个页面进行修改任务执行时间的值,其实也挺爽。

这种方案其实可以称为是简易版的 Quartz,在一定程度上也能解决一定的业务场景问题,但是若做更复杂的动作,例如启停任务、删除任务等等操作,实现起来则稍显复杂,此时便可以通过集成 Quartz 等开源任务框架来实现,而鉴于集成 Quartz 框架的动态管理任务代码较多咱们下一篇再分享。

3. 例行回顾

本文是 Spring Boot 项目集成定时任务首篇讲解,主要分享了如下部分:

  • Spring Boot 内置注解实现静态定时任务;
  • 提了一嘴四种任务时间配置格式;
  • 分享了如何开启多线程跑任务?
  • 尝试实现了动态定时任务。

玩转 Spring Boot 集成定时任务首篇就写到这里,下次一起集成 Quratz 框架并实现任务动态管理。

历史系列文章:

玩转 Spring Boot 入门篇 玩转 Spring Boot 集成篇(MySQL、Druid、HikariCP) 玩转 Spring Boot 集成篇(MyBatis、JPA、事务支持) 玩转 Spring Boot 集成篇(Redis) 玩转 Spring Boot 集成篇(Actuator、Spring Boot Admin) 玩转 Spring Boot 集成篇(RabbitMQ)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一猿小讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
# 访问蓝色通道 blue_channel = img[:, :, 0] # 修改红色通道 img[:, :, 2] = 0 up主你好 我是初学者,这里应该是注释写错了 ,
# 访问蓝色通道 blue_channel = img[:, :, 0] # 修改红色通道 img[:, :, 2] = 0 up主你好 我是初学者,这里应该是注释写错了 ,
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务)(七)
研发说:API 请求量到底啥情况呀?统计发粗来(万一访问量一直激增,导致服务宕了,要扣我绩效滴)。
botkenni
2022/09/23
1.3K0
玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务)(七)
Spring Boot 如何实现定时任务
在现代的微服务架构中,定时任务是一个常见的需求。无论是数据定时同步、定时清理缓存,还是定时发送通知,Spring Boot 提供了非常强大且灵活的定时任务支持。本文将详细介绍如何在 Spring Boot 中实现定时任务,包括使用 @Scheduled 注解和集成 Quartz 调度框架的两种方式。
全干程序员demo
2025/05/29
2250
Spring Boot 实现定时任务的 4 种方式
Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
Java技术栈
2019/06/14
13.6K0
Spring Boot 实现定时任务的 4 种方式
在Spring Boot中优雅的实现定时任务
在日常的项目开发中,往往会涉及到一些需要做到定时执行的代码,例如自动将超过24小时的未付款的单改为取消状态,自动将超过14天客户未签收的订单改为已签收状态等等,那么为了在Spring Boot中实现此类需求,我们要怎么做呢?
用户2781897
2020/11/02
1.2K0
在Spring Boot中优雅的实现定时任务
SpringBoot开发案例之整合定时任务(Scheduled)
来来来小伙伴们,基于上篇的邮件服务,定时任务就不单独分项目了,天然整合进了邮件服务中。
小柒2012
2018/03/30
1.2K5
SpringBoot开发案例之整合定时任务(Scheduled)
Spring Boot多线程环境下,解决多个定时任务冲突问题
关于定时任务注解@Scheduled在之前的文章已经讲到,Spring Boot定时器默认的是单线程的。
Java技术债务
2022/08/09
1.2K0
【Spring】定时任务详解实例-@Scheduled
最近在做项目,时间比较紧张,也有比较久没写博客了。 现在项目的Redis缓存需要用到定时任务,就学习了一下Spring 的@Scheduled注解。使用起来很简单。
谙忆
2021/01/21
8290
【Spring】定时任务详解实例-@Scheduled
玩转 Spring Boot 集成篇(定时任务框架Quartz)
在日常项目研发中,定时任务可谓是必不可少的一环,关于 Spring Boot 如何实现静态定时任务、动态定时任务以及如何开启多线程跑任务,均已在上篇分享过,不再赘述。
一猿小讲
2022/02/25
23.9K2
玩转 Spring Boot 集成篇(定时任务框架Quartz)
springboot自带定时任务和集成quartz
1,springboot自带的定时任务 默认是单线程 有这个依赖就可以 <dependency> <groupId>org.springframework.boot</groupId>
大数据流动
2019/08/08
1.8K0
玩转 Spring Boot 集成篇(任务动态管理代码篇)
在日常项目研发中,定时任务可谓是必不可少的一环,如果面对任务执行周期固定,业务简单的场景,可直接使用 Spring Boot 内置注解方式实现任务;而如果考虑更为复杂的管理任务信息,在可以通过集成 Quartz 等开源轮子来助力业务研发。
一猿小讲
2022/02/25
2.3K9
玩转 Spring Boot 集成篇(任务动态管理代码篇)
Spring @Scheduled 怎么使用才正确
Spring scheduled 默认为单线程,最近就发现生产上有某个服务实例不按时执行 task 任务,最后排查出使用了@Scheduled,发现一个任务执行12个小时,导致其他任务都在排队等待,没有在规定时间去抢占分布式锁。
BUG弄潮儿
2023/02/24
3.2K0
Spring @Scheduled 怎么使用才正确
SpringBoot---(15)Spring Boot创建定时任务
摘要:项目中经常会需要做一些定时的跑的事情,比如每间隔多久做个统计,发个邮件,清理个数据。这时候就要用到定时任务,SpringBoot中,创建定时任务非常简单,具体步骤如下:
IT云清
2019/01/22
5150
@scheduled注解 定时任务控制(Spring响应式编程)
项目经常会用到定时任务,实现定时任务的方式有很多种。在Spring框架中,实现定时任务很简单,常用的实现方式是使用注解@Scheduled。
全栈程序员站长
2022/07/31
7640
@scheduled注解 定时任务控制(Spring响应式编程)
Java之定时任务全家桶
由此看出走不同的线程执行,不同的线程执行的好处是,如果某一个线程挂掉后,并不会阻塞导致其它定时任务无法执行。
BUG弄潮儿
2021/03/04
6140
Spring Boot几种定时任务的实现方式
在实际开发中,我们多多少少都会用到一些定时任务的场景,本文来聊一下常用的定时任务。
田维常
2022/11/25
6540
Spring Boot几种定时任务的实现方式
Spring Boot: 定时任务注解@Scheduled以及动态任务SchedulingConfigurer 实践
cronExpression定义时间规则,Cron表达式由6或7个空格分隔的时间字段组成:秒 分钟 小时 日期 月份 星期 年(可选)
Freedom123
2024/03/29
2.8K0
Spring Boot: 定时任务注解@Scheduled以及动态任务SchedulingConfigurer 实践
@Scheduled 多个定时任务同时执行
这篇文章主要介绍了springBoot @Scheduled实现多个任务同时开始执行,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
猫头虎
2024/04/08
1.3K0
SpringBoot2.0 基础案例(04):定时任务和异步任务的使用方式
同步调用 程序按照代码顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行; 异步调用 顺序执行时,不等待异步调用的代码块返回结果就执行后面的程序。
知了一笑
2019/07/19
7630
玩转SpringBoot之定时任务详解
基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。
java架构师
2019/03/06
2.4K0
玩转SpringBoot之定时任务详解
后端技术:Java定时任务的五种创建方式
job任务名:@JobHandler注解值 >> 如:@JobHandler(value=“demoJobHandler”)
小明互联网技术分享社区
2021/09/09
9750
推荐阅读
相关推荐
玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务)(七)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验