[075][示例]基于自定义的定时任务框架实战详解

简介: 本文详解`tutorials4j`自定义定时任务框架实战,涵盖YAML灵活配置(Cron、初始延迟、执行/失败次数限制)、`TaskRunner`任务实现、`TaskRuntimeDataHandler`状态监听及线程池管理,支持单机高可用与分布式扩展,提升调度可观测性与健壮性。(239字)

[075][示例]基于自定义的定时任务框架实战详解

本文章代码: gitee , gitcode , github

1. 引言

在企业级应用中,定时任务(Scheduled Tasks)是不可或缺的基础设施,用于数据同步、报表生成、系统监控等场景。tutorials4j 是一个功能丰富的 Java 框架,其 Schedule 模块 在 Spring Boot 原生调度能力之上,提供了更强大的任务管理、状态监控、异常处理和配置灵活性。

本文通过一个完整的示例项目(examples-schedule),深入分析其核心功能、配置方式、任务实现、运行时状态处理以及日志分析,帮助读者快速掌握该框架的使用方法和最佳实践。


2. 示例项目概览

该示例是一个 Spring Boot 应用,启动类为 ExampleScheduleApplication,激活的 profile 为 schedule。它定义了四个定时任务(其中两个共用同一个任务类),并通过 application-schedule.yml 进行集中配置。主要组件包括:

  • 任务类:Demo1Task、Demo2Task、Demo3Task,均实现 TaskRunner 接口。
  • 状态处理器:DemoTaskRuntimeDataHandler,实现 TaskRuntimeDataHandler,监听任务生命周期事件。
  • 配置文件:application-schedule.yml,声明任务元数据、执行策略和 Spring 线程池参数。

3. 配置详解:灵活的任务声明

application-schedule.yml 是调度框架的核心配置文件,它采用分层结构,支持每个任务独立配置。

3.1 全局配置

tutorials4j:
  schedule:
    spring:
      enabled: true
      all-task-auto-start-on-boot: true   # 应用启动时自动注册所有任务
      tasks:                              # 任务列表
        ...
  • enabled:总开关,可一键禁用调度功能。
  • all-task-auto-start-on-boot:默认为 true,表示 Spring 容器启动后自动将配置的任务加载到调度器中。

3.2 单个任务配置详解

以 demo1Task 为例:

demo1Task:
  enabled: true
  cron: 0/5 * * * * ?          # 每5秒执行一次
  class-simple-name: demo1Task # 对应 Spring Bean 名称(首字母小写)
  description: 测试任务1
  metadata:                    # 自定义元数据,传递给任务参数
    key1: value1
  • cron:标准的 Cron 表达式,支持秒级精度。
  • class-simple-name:指容器中 TaskRunner 实现类的 Bean 名称,框架通过该名称查找并执行。
  • metadata:键值对,会作为 Map<String,String> 参数传入 run 方法,方便任务获取自定义配置。

3.3 高级执行策略

框架提供了三种增强的控制参数:

initial-delay

首次执行前的延迟时间,单位支持 s(秒)、ms(毫秒)等。例如 demo2Task 配置了 initial-delay: 10s,表示启动后等待10秒才触发第一次执行。

max-execution-count

最大执行次数,达到后任务自动停止。demo2Task 设置为 3,意味着它只执行三次(无论成功或失败),之后不再触发。

max-failure-count

最大连续失败次数,达到后任务被停止(状态变为 STOPPED)。demo3Task 设置为 3,若连续失败三次,框架会自动禁用该任务,避免无意义的重试。

这些策略使得任务管理更加健壮,尤其适合需要限流或降级的场景。


4. 任务实现:模拟业务逻辑与异常

三个任务类都实现了 TaskRunner 接口,其 run(Map<String, String> params) 方法是执行入口。

4.1 Demo1Task:基础任务

@Slf4j
@Component
public class Demo1Task implements TaskRunner {
   
    @Override
    public void run(Map<String, String> params) {
   
        log.info(">>> demo1, {}, {}, {}", 
                 Thread.currentThread().getName(), params, System.currentTimeMillis());
        // 随机休眠 0~3 秒,模拟耗时操作
        long milli = ThreadLocalRandom.current().nextInt(3000);
        TimeUnit.MILLISECONDS.sleep(milli);
    }
}
  • 打印当前线程名、传入参数和时间戳。
  • 随机休眠,模拟不同的执行时长,便于观察并发调度情况。

4.2 Demo2Task:可抛出异常的任务

@Override
public void run(Map<String, String> params) {
   
    // 随机休眠 0~4 秒
    // ... 
    int num = ThreadLocalRandom.current().nextInt(100);
    if (num >= 70) {
      // 30% 概率抛出异常
        throw new RuntimeException("任务异常");
    }
}
  • 以 30% 的概率主动抛出运行时异常,模拟业务失败。
  • 该任务配置了 max-execution-count=3,因此即便失败,也只执行三次。

4.3 Demo3Task:带失败次数限制的任务

demo3Task:
  cron: 0/6 * * * * ?
  max-failure-count: 3
  • 任务本身不主动抛异常(仅随机休眠),但在实际生产环境中,失败可能来自外部依赖。
  • 若连续失败三次,框架会将其状态置为 STOPPED,停止后续调度。

5. 运行时状态监控:TaskRuntimeDataHandler

框架内置了一套状态事件机制,允许开发者通过实现 TaskRuntimeDataHandler 接口来感知任务的完整生命周期。

@Slf4j
@Component
public class DemoTaskRuntimeDataHandler implements TaskRuntimeDataHandler {
   
    @Override
    @Async  // 异步处理,不阻塞调度线程
    public void handle(TaskRuntimeData data) {
   
        log.info("任务实时运行状态:{}", data.taskStatus());
    }
}
  • 每个任务在状态变更时(如 CREATED、STARTED、COMPLETED、EXCEPTION、STOPPED)都会触发该处理方法。
  • 通过 @Async 注解,状态处理与任务执行解耦,避免影响调度性能。

从运行日志中可以看到状态流转的完整轨迹:

任务实时运行状态:CREATED   # 任务注册时
任务实时运行状态:STARTED   # 任务开始执行
任务实时运行状态:COMPLETED # 正常结束
任务实时运行状态:EXCEPTION # 执行抛出异常
任务实时运行状态:STOPPED   # 达到失败次数限制或执行次数限制时

这种设计非常便于集成告警、日志聚合或执行历史记录系统。


6. 日志分析:任务执行全景

我们从 log.txt 中截取关键片段,观察任务如何按调度计划执行。

6.1 启动与注册

2026-06-28 22:01:50.377 DEBUG - 添加定时任务, taskCode=demo3Task
2026-06-28 22:01:50.387 DEBUG - 添加定时任务, taskCode=demo1Task
2026-06-28 22:01:50.387 INFO  - 任务实时运行状态:CREATED   # demo1Task
2026-06-28 22:01:50.387 DEBUG - 添加定时任务, taskCode=demo1Task_1
2026-06-28 22:01:50.387 INFO  - 任务实时运行状态:CREATED   # demo1Task_1
2026-06-28 22:01:50.388 DEBUG - 添加定时任务, taskCode=demo2Task
2026-06-28 22:01:50.388 INFO  - 任务实时运行状态:CREATED   # demo2Task
2026-06-28 22:01:50.389 INFO  - 任务实时运行状态:CREATED   # demo3Task

所有任务在启动时被注册,并立即触发 CREATED 事件。

6.2 demo2Task 的异常与执行次数限制

22:02:01.009 INFO  - demo2, example-scheduler-1, {}, 1782655321009  # 第一次执行
22:02:01.010 INFO  - 任务实时运行状态:STARTED
22:02:04.719 INFO  - 任务实时运行状态:EXCEPTION
22:02:04.719 ERROR - Unexpected error occurred in scheduled task
java.lang.RuntimeException: 任务异常
...
22:02:06.000 INFO  - demo2, example-scheduler-4, {}, 1782655326000  # 第二次执行
22:02:07.115 INFO  - 任务实时运行状态:EXCEPTION
...
22:02:09.001 INFO  - demo2, example-scheduler-5, {}, 1782655329001  # 第三次执行
22:02:09.001 INFO  - 任务实时运行状态:STARTED
22:02:11.935 INFO  - 任务实时运行状态:COMPLETED  # 第三次成功,但达到最大执行次数
22:02:11.935 INFO  - 任务实时运行状态:STOPPED
  • 第一次和第二次执行抛出异常,第三次执行成功,但由于 max-execution-count=3,任务在完成后立即被标记为 STOPPED,后续不再调度。日志中之后也确实没有 demo2Task 的任何日志。

6.3 demo3Task 的失败计数与停止

虽然示例中 demo3Task 未主动抛出异常,但若其多次失败(比如模拟外部服务故障),一旦连续失败次数达到 max-failure-count=3,框架会自动将其停止,日志中会出现 STOPPED 状态。这为生产环境的自我保护提供了有效手段。

6.4 线程池与并发执行

spring:
  task:
    scheduling:
      pool:
        size: 5
      thread-name-prefix: example-scheduler-

从日志可见,任务执行线程名为 example-scheduler-1 ~ example-scheduler-5,共 5 个线程。当多个任务的 Cron 触发时间重叠时,它们会被分配给不同的线程并发执行,互不影响。


7. 高级特性与扩展点

  • 元数据传递:metadata 允许为同一任务类配置不同的参数,如 demo1Task 和 demo1Task_1 使用同一个类但传递不同键值对,实现配置复用。
  • 异步状态处理:DemoTaskRuntimeDataHandler 使用 @Async,可以结合自己的线程池,避免处理逻辑拖慢主调度。
  • 可插拔的任务仓库:框架支持从 YAML、数据库或配置中心加载任务定义,示例中使用的是 YAML 方式。
  • 与分布式调度集成:tutorials4j 还提供了 Redis、Redisson、PowerJob、XXL-JOB 等适配,方便从单机迁移到分布式环境。

8. 总结

通过分析 examples-schedule 示例,我们全面了解了 tutorials4j 调度框架的核心能力:

  1. 灵活的配置:通过 YAML 集中管理任务,支持 Cron、固定延迟、初始延迟、执行次数和失败次数限制。
  2. 任务实现简单:只需实现 TaskRunner 接口,并注入为 Spring Bean。
  3. 完善的状态监控:TaskRuntimeDataHandler 可捕获所有状态变更,便于日志、告警和运维。
  4. 健壮的异常处理:内置失败计数和自动停止机制,防止故障任务反复消耗资源。
  5. 可扩展性强:与多种分布式调度组件无缝集成,满足从单体到微服务的演进需求。

该框架不仅降低了定时任务开发的门槛,更通过标准化的设计提升了系统的可观测性和稳定性。建议开发者在实际项目中借鉴其设计理念,构建更可靠的调度体系。


附录:示例代码中的关键配置和类均已整理如上,读者可结合自己的业务需求进行定制。如需深入了解源码,可查阅 tutorials4j 官方文档或 GitHub 仓库。

目录
相关文章
|
23天前
|
人工智能 自然语言处理 安全
Geo优化中AI偏好哪类内容?科普与深度解读
本文解析国产大模型生成式回答的底层机制(RAG),揭示内容被引用的真实逻辑:非整篇阅读,而是“片段截取”——关键在能否独立回答子问题。结合实测数据与Geo专家于磊的实战经验,系统梳理科普、深度解读、行业分析等六类内容的引用规律,并提出“结论前置、数字带源、出处可验”等十条可落地建议,助力创作者提升AI入口可见度。
108 3
|
3月前
|
缓存 监控 安全
[025][Web模块]基于 Spring Boot 的请求日志过滤器设计与实现
本文介绍基于Spring Boot的可配置请求日志过滤器,通过自定义`WebHttpProperties`、扩展`CommonsRequestLoggingFilter`及自动配置类,支持时间戳、客户端信息、请求头/体记录、动态前缀等灵活配置,开箱即用,兼顾可维护性与生产安全性。(239字)
446 1
|
21天前
|
JSON API 数据格式
没有海外信用卡,怎么用上 Claude Code:四条路径和一个常见误区(2026 年 9 月)
「没有海外信用卡就用不了 Claude Code」其实把三件事捆在了一起:支付是商务问题、网络是工程问题、条款是法律问题——只有前两个能换路径解决。本文先拆问题,再给四条路径(国内云厂商、国产模型套餐、国内订阅制服务、已有海外主体),最后说明「以为搞到一张能付款的卡就解决了」为什么是误区。
|
13天前
|
人工智能 自然语言处理 API
阿里云百炼官网入口在哪?百炼AI模型官网和百炼控制台(后台)一键直达
阿里云百炼是阿里云推出的企业级一站式AI大模型服务平台,集成Qwen全系列及DeepSeek、Kimi等百余款主流大模型,支持文本、图像、视频、语音等全模态能力,提供API调用、智能体开发、知识库(RAG)与低代码工作流,开通即赠超7000万Tokens,90天有效。
|
1月前
|
Web App开发 JavaScript Windows
【bug反馈】Qoder 浏览器连接器(Windows)侧边栏持续显示"未发现客户端"(内有详细原因和修复方案)
Qoder浏览器连接器在Windows上因Chrome调用`.cmd`宿主时无控制台导致通信失败,表现为“未发现客户端”。根本原因是`cmd.exe`在无控制台+管道重定向下无法执行。建议改用console子系统shim进程直启主程序。
394 2
|
3月前
|
JSON 安全 Java
[024][Web模块]基于 AntiSamy 的 Spring Boot XSS 防护实践:从过滤器到反序列化的多层防御
本文介绍基于OWASP AntiSamy的Spring Boot XSS多层防护方案:通过Servlet过滤器清洗表单/查询参数,结合Jackson反序列化器净化JSON数据,实现非侵入、全覆盖的输入清洗,业务代码零修改。(239字)
427 2
|
3月前
|
前端开发 Java API
[049][Crypto模块]前后端混合加密API实战:基于Spring Boot的AES+RSA安全传输方案
本文详解Spring Boot中AES+RSA混合加密实战:前端用RSA公钥加密随机AES密钥并传输,后端通过`@Crypto`注解自动解密请求体。涵盖公钥分发、Hex/Base64编码统一、ECB模式适配及Caffeine缓存优化,提供开箱即用的端到端安全传输方案。(239字)
363 0
|
5月前
|
缓存 运维 Java
[014][web模块]构建可重复读取的请求体:Spring Boot 请求缓存过滤器设计与实现
本文介绍Spring Boot中实现可重复读取请求体的缓存过滤器方案,通过`HttpServletRequestWrapper`包装、内存缓存与条件过滤,解决流式请求体只能读一次的痛点,支持日志、验签等多场景复用,具备自动配置、长度限制、路径匹配等特性,轻量透明,开箱即用。(239字)
240 1
|
5月前
|
缓存 安全 搜索推荐
[004][缓存模块]Caffeine缓存自定义:构建灵活的Spring Boot缓存管理器
本文介绍Spring Boot中Caffeine缓存的灵活定制方案:通过自定义`FlexibleCaffeineCacheManager`,支持按缓存名(如users/products)独立配置过期策略、容量等参数,兼顾全局默认与个性化需求;结合线程安全创建器、属性合并机制及无缝Spring集成,实现高性能、易扩展、零侵入的本地缓存管理。(239字)
275 2
|
5月前
|
缓存 NoSQL Java
[006][缓存模块] 两级缓存实战:基于 Caffeine + Redis 的多级缓存设计与实现
本文介绍基于Caffeine(本地)+ Redis(分布式)的两级缓存实战方案,通过自定义`MultiLevelCache`与`MultiLevelCacheManager`,实现Spring Cache标准接口下的透明多级缓存:读优先本地(纳秒级)、未命中查Redis并回填;写同步更新两级,兼顾高性能与数据共享。代码开源可直接集成。
409 0