Spring Boot 2.X(十二):定时任务

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 简介定时任务是后端开发中常见的需求,主要应用场景有定期数据报表、定时消息通知、异步的后台业务逻辑处理、日志分析处理、垃圾数据清理、定时更新缓存等等。Spring Boot 集成了一整套的定时任务工具,让我们专注于完成逻辑,剩下的基础调度工作将自动完成。

简介

定时任务是后端开发中常见的需求,主要应用场景有定期数据报表、定时消息通知、异步的后台业务逻辑处理、日志分析处理、垃圾数据清理、定时更新缓存等等。

Spring Boot 集成了一整套的定时任务工具,让我们专注于完成逻辑,剩下的基础调度工作将自动完成。

通用实现方式

实现方式 描述
java.util.Timer Timer 提供了一个 java.util.TimerTask 任务支持任务调度。该方式只能按指定频率执行,不能在指定时间运行。由于功能过于单一,使用较少。
Quartz Quartz 是一个功能比较强大的调度器,支持在指定时间运行,也可以按照指定频率执行。缺点是使用起来相对麻烦。
Spring 框架自带的 Schedule 模块 可以看做是轻量级的 Quartz

静态定时任务

@Component
@EnableScheduling
public class StaticScheduleJob {
    
    /**
     * 上次开始执行时间点后5秒再次执行
     */
    @Scheduled(fixedRate = 5000)
    public void job3() {
        System.out.println("执行任务job3:"+DateUtil.formatDateTime(new Date()));
    }
    
    /**
     * 上次执行完毕时间点后3秒再次执行
     */
    @Scheduled(fixedDelay = 3000)
    public void job2() {
        System.out.println("执行任务job2:"+DateUtil.formatDateTime(new Date()));
    }
    
    /**
     * 每隔10秒执行一次(按照 corn 表达式规则执行)
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void job1() {
        System.out.println("执行任务job1:"+DateUtil.formatDateTime(new Date()));
    }

    
}

@EnableScheduling 注解启用定时调动功能

@Scheduled 参数说明:

  • @Scheduled(fixedRate = 5000):上次开始执行时间点后5秒再次执行
  • @Scheduled(fixedDelay = 3000):上次执行完毕时间点后3秒再次执行
  • @Scheduled(cron = "0/10 ?"):每隔10秒执行一次(按照 corn 表达式规则执行)

Cron 表达式

1.Cron表达式格式

{秒} {分} {时} {日} {月} {周} {年(可选)}

2.Cron 表达式字段取值范围及说明

字段 取值范围 允许的特殊字符
Seconds(秒) 0 ~ 59 , - * /
Minutes(分) 0 ~ 59 , - * /
Hours(时) 0 ~ 23 , - * /
Day-of-Month(天) 可以用数字 1 ~ 31 中的任意一个值,但要注意特殊月份 , - * ? / L W C
Month(月) 可以用 0 ~ 11 或者字符串 “JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV、DEC” 表示 , - * /
Day-of-Week(每周) 可以用数字 1 ~ 7 表示(1=星期日)或者用字符串 “SUN、MON、TUE、WED、THU、FRI、SAT” 表示 , - * ? / L C #
Year(年) 取值范围(1970-2099),允许为空值 , - * /

3.Cron 表达式中特殊字符的意义

  • | 表示可以匹配该域的所有值
    ?| 主要用于日和星期,可以匹配域的任意值,但实际不会。当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为?

/ | 表示为每隔一段时间。如 0 0/10 * ? 其中的 0/10表示从0分钟开始,每隔10分钟执行一次

  • | 表示范围。如 0 0-5 14 ? 表示在每天14:00到14:05期间每1分钟执行一次
    , | 表示枚举多个值,这些值之间是"或"的关系。如 0 10,30 14 * 3 ? 表示每个星期二14点10分或者14点30分执行一次

L | 表示每月或者每周的最后一天。如 0 0 0 L ? 表示每月的最后一天执行
W | 表示最近工作日。如 0 0 0 15W 表示每月15号最近的那个工作日执行

| 用来指定具体的周数,"#"前面代表星期,"#"后面代表本月的第几周。如"2#1"表示本月第二周的星期日

4.Cron 在线生成工具

http://www.bejson.com/othertools/cron/

动态定时任务

1.实现 SchedulingConfigurer 接口

@Configuration
public class CustomScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private CronTriggerDao cronTriggerDao;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 上次开始执行时间点后5秒再执行
        taskRegistrar.addFixedRateTask(() -> System.out.println("CustomScheduleConfig执行任务job1=>"
                + DateUtil.formatDateTime(new Date()) + ",线程:" + Thread.currentThread().getName()), 5000);
        // 上次执行完毕时间点后3秒再执行
        taskRegistrar.addFixedDelayTask(() -> System.out.println("CustomScheduleConfig执行任务job2=>"
                + DateUtil.formatDateTime(new Date()) + ",线程:" + Thread.currentThread().getName()), 3000);
        // 添加一个配合数据库动态执行的定时任务
        TriggerTask triggerTask = new TriggerTask(
                // 任务内容.拉姆达表达式
                () -> {
                    // 1)添加任务 Runnable
                    System.out.println("CustomScheduleConfig执行动态任务job3=>" + DateUtil.formatDateTime(new Date()) + ",线程:"
                            + Thread.currentThread().getName());
                    // 2)设置执行周期
                }, triggerContext -> {
                    // 3)从数据库获取执行周期
                    String cron = cronTriggerDao.getCronTriggerById(1L);
                    if (cron != null) {
                        // 4)返回执行周期(Date)
                        return new CronTrigger(cron).nextExecutionTime(triggerContext);
                    } else {
                        return null;
                    }

                });
        taskRegistrar.addTriggerTask(triggerTask);

    }

}

2.数据库中初始化数据

这边为了测试简单初始化两行数据:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_cron_trigger
-- ----------------------------
DROP TABLE IF EXISTS `t_cron_trigger`;
CREATE TABLE `t_cron_trigger` (
  `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '任务id',
  `cron` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT 'cron表达式',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `is_deleted` int(1) DEFAULT '0' COMMENT '作废状态 0-否 1-是',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records of t_cron_trigger
-- ----------------------------
BEGIN;
INSERT INTO `t_cron_trigger` VALUES (1, '0/10 * * * * ?', '2019-10-30 13:40:14', NULL, 0);
INSERT INTO `t_cron_trigger` VALUES (2, '0/7 * * * * ?', '2019-10-30 13:40:34', NULL, 0);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

3.从数据库中读取 cron 表达式值

CronTrigger 实体类

public class CronTrigger implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 880141459783509786L;
    
    private Long id;//任务id
    private String cron;//cron表达式
    private String createTime;//创建时间
    private String updateTime;//更新时间
    private boolean isDeleted=false;//删除状态
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getCron() {
        return cron;
    }
    public void setCron(String cron) {
        this.cron = cron;
    }
    public String getCreateTime() {
        return createTime;
    }
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    public String getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }
    public boolean isDeleted() {
        return isDeleted;
    }
    public void setDeleted(boolean isDeleted) {
        this.isDeleted = isDeleted;
    }
}

CronTriggerDao

public interface CronTriggerDao {
    /**
     * 根据id获取cron表达式
     * @param id
     * @return
     */
    String getCronTriggerById(Long id);

}

CronTriggerMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zwqh.springboot.dao.CronTriggerDao">
    <resultMap type="cn.zwqh.springboot.model.CronTrigger" id="cronTrigger">
        <id property="id" column="id"/>
        <result property="cron" column="cron"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
        <result property="isDeleted" column="is_deleted"/>
    </resultMap>
    <!-- 根据id获取cron表达式 -->
    <select id="getCronTriggerById" resultType="String">
        select cron from t_cron_trigger where is_deleted=0 and id=#{id}
    </select>
</mapper>

4.测试

启动前记得在启动类上加上 @EnableScheduling

打印日志如下:

多线程定时任务

通过上面的日志我们可以看到任务执行都是单线程的。如果要实现多线程执行任务,我们可以通过在 SchedulingConfigurer 接口的 configureTasks方法中添加线程池即可。

1.CustomScheduleConfig

@Configuration
public class CustomScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private CronTriggerDao cronTriggerDao;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        
        //设定一个长度为10的定时任务线程池
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
        
        // 上次开始执行时间点后5秒再执行
        taskRegistrar.addFixedRateTask(() -> System.out.println("CustomScheduleConfig执行任务job1=>"
                + DateUtil.formatDateTime(new Date()) + ",线程:" + Thread.currentThread().getName()), 5000);
        // 上次执行完毕时间点后3秒再执行
        taskRegistrar.addFixedDelayTask(() -> System.out.println("CustomScheduleConfig执行任务job2=>"
                + DateUtil.formatDateTime(new Date()) + ",线程:" + Thread.currentThread().getName()), 3000);
        // 添加第一个配合数据库动态执行的定时任务
        TriggerTask triggerTask = new TriggerTask(
                // 任务内容.拉姆达表达式
                () -> {
                    // 1)添加任务 Runnable
                    System.out.println("CustomScheduleConfig执行动态任务job3=>" + DateUtil.formatDateTime(new Date()) + ",线程:"
                            + Thread.currentThread().getName());
                    // 2)设置执行周期
                }, triggerContext -> {
                    // 3)从数据库获取执行周期
                    String cron = cronTriggerDao.getCronTriggerById(1L);
                    if (cron != null) {
                        // 4)返回执行周期(Date)
                        return new CronTrigger(cron).nextExecutionTime(triggerContext);
                    } else {
                        return null;
                    }

                });
        taskRegistrar.addTriggerTask(triggerTask);
        // 添加第二个配合数据库动态执行的定时任务
        TriggerTask triggerTask2 = new TriggerTask(
                // 任务内容.拉姆达表达式
                () -> {
                    // 1)添加任务 Runnable
                    System.out.println("CustomScheduleConfig执行动态任务job4=>" + DateUtil.formatDateTime(new Date()) + ",线程:"
                            + Thread.currentThread().getName());
                    // 2)设置执行周期
                }, triggerContext -> {
                    // 3)从数据库获取执行周期
                    String cron = cronTriggerDao.getCronTriggerById(2L);
                    if (cron != null) {
                        // 4)返回执行周期(Date)
                        return new CronTrigger(cron).nextExecutionTime(triggerContext);
                    } else {
                        return null;
                    }

                });
        taskRegistrar.addTriggerTask(triggerTask2);

    }

}

2.测试

打印日志如下:

示例代码

github

码云

非特殊说明,本文版权归 朝雾轻寒 所有,转载请注明出处.

原文标题:Spring Boot 2.X(十二):定时任务

原文地址: https://www.zwqh.top/article/info/21

如果文章对您有帮助,请扫码关注下我的公众号,文章持续更新中...

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
129 1
|
12天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
25 2
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
56 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
Java BI 调度
Java Spring的定时任务的配置和使用
遵循上述步骤,你就可以在Spring应用中轻松地配置和使用定时任务,满足各种定时处理需求。
139 1
|
1月前
|
存储 Java API
简单两步,Spring Boot 写死的定时任务也能动态设置:技术干货分享
【10月更文挑战第4天】在Spring Boot开发中,定时任务通常通过@Scheduled注解来实现,这种方式简单直接,但存在一个显著的限制:任务的执行时间或频率在编译时就已经确定,无法在运行时动态调整。然而,在实际工作中,我们往往需要根据业务需求或外部条件的变化来动态调整定时任务的执行计划。本文将分享一个简单两步的解决方案,让你的Spring Boot应用中的定时任务也能动态设置,从而满足更灵活的业务需求。
100 4
|
1月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
64 2
|
4月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
Spring Cloud Alibaba 发布了 Scheduling 任务调度模块 [#3732]提供了一套开源、轻量级、高可用的定时任务解决方案,帮助您快速开发微服务体系下的分布式定时任务。
14978 29
|
3月前
|
Java 开发者 Spring
Spring Boot实战宝典:揭秘定时任务的幕后英雄,让业务处理如流水般顺畅,轻松驾驭时间管理艺术!
【8月更文挑战第29天】在现代应用开发中,定时任务如数据备份、报告生成等至关重要。Spring Boot作为流行的Java框架,凭借其强大的集成能力和简洁的配置方式,为开发者提供了高效的定时任务解决方案。本文详细介绍了如何在Spring Boot项目中启用定时任务支持、编写定时任务方法,并通过实战案例展示了其在业务场景中的应用,同时提供了注意事项以确保任务的正确执行。
53 0
|
3月前
|
Dubbo Java 调度
揭秘!Spring Cloud Alibaba的超级力量——如何轻松驾驭分布式定时任务调度?
【8月更文挑战第20天】在现代微服务架构中,Spring Cloud Alibaba通过集成分布式定时任务调度功能解决了一致性和可靠性挑战。它利用TimerX实现任务的分布式编排与调度,并通过`@SchedulerLock`确保任务不被重复执行。示例代码展示了如何配置定时任务及其分布式锁,以实现每5秒仅由一个节点执行任务,适合构建高可用的微服务系统。
70 0
|
4月前
|
SQL Java 调度
实时计算 Flink版产品使用问题之使用Spring Boot启动Flink处理任务时,使用Spring Boot的@Scheduled注解进行定时任务调度,出现内存占用过高,该怎么办
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
下一篇
无影云桌面