# tz-module-quartz 独立定时任务服务 ## 📋 项目概述 创建了一个独立的Quartz定时任务服务模块 `tz-module-quartz`,参考 `tz-module-mq` 的结构,用于解决多节点部署时定时任务重复执行的问题。 ## 🎯 核心设计理念 ### 为什么需要独立的Quartz服务? 1. **避免重复执行**: 多个业务节点部署时,每个节点的定时任务都会执行,导致数据重复 2. **集中管理**: 所有定时任务统一在一个服务中调度,便于管理和监控 3. **解耦业务**: 定时任务逻辑与业务逻辑分离,降低耦合度 4. **资源优化**: 不需要每个业务节点都运行定时任务 ### 架构设计 ``` ┌─────────────────────────────────────────────────────┐ │ tz-module-quartz (独立服务) │ │ ┌──────────────────────────────────────────────┐ │ │ │ Quartz Scheduler (调度器) │ │ │ │ - 任务调度 │ │ │ │ - 集群锁(防重复执行) │ │ │ │ - 失败重试 │ │ │ └──────────────────────────────────────────────┘ │ │ ↓ │ │ ┌──────────────────────────────────────────────┐ │ │ │ JobManager (任务管理器) │ │ │ │ - 增删改查任务 │ │ │ │ - 暂停/恢复任务 │ │ │ │ - 手动触发 │ │ │ └──────────────────────────────────────────────┘ │ │ ↓ │ │ ┌──────────────────────────────────────────────┐ │ │ │ InvokerJob (任务执行器) │ │ │ │ - 调用具体的JobHandler │ │ │ │ - 记录执行日志 │ │ │ │ - 异常处理 │ │ │ └──────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘ ↓ RPC调用 ┌─────────────────────────────────────────────────────┐ │ 业务服务 (tz-module-pressure等) │ │ ┌──────────────────────────────────────────────┐ │ │ │ JobHandler (任务处理器) │ │ │ │ - AppointmentPushJobHandler │ │ │ │ - SyncTaskEquipmentJobHandler │ │ │ │ - ReportPushJobHandler │ │ │ └──────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘ ``` ## 📁 已创建的文件结构 ``` tz-module-quartz/ ├── pom.xml # 模块POM └── tz-module-quartz-biz/ # 业务模块 ├── pom.xml # 业务模块POM ├── src/main/java/cn/start/tz/module/quartz/ │ ├── QuartzServerApplication.java # 启动类 │ ├── dal/ │ │ ├── dataobject/ │ │ │ └── job/ │ │ │ └── JobDO.java # 定时任务DO │ │ └── mysql/ │ │ └── job/ │ │ └── JobMapper.java # Mapper │ ├── framework/ │ │ └── quartz/ │ │ ├── JobManager.java # 任务管理器 │ │ ├── InvokerJob.java # 任务执行器 │ │ └── JobHandler.java # 任务处理器接口 │ └── service/ │ └── job/ │ └── JobService.java # 任务Service接口 └── src/main/resources/ ├── application.yaml # 配置文件 └── logback-spring.xml # 日志配置 ``` ## 🔧 核心组件说明 ### 1. JobManager (任务管理器) - **位置**: `framework/quartz/JobManager.java` - **功能**: - 添加/更新/删除任务 - 暂停/恢复任务 - 手动触发任务 - 查询所有任务 - **特性**: 使用Quartz原生API管理任务生命周期 ### 2. InvokerJob (任务执行器) - **位置**: `framework/quartz/InvokerJob.java` - **功能**: - 接收Quartz调度 - 根据handlerName查找对应的JobHandler - 执行任务并记录日志 - **注解**: `@DisallowConcurrentExecution` 禁止并发执行 ### 3. JobHandler (任务处理器接口) - **位置**: `framework/quartz/JobHandler.java` - **功能**: 定义任务处理器标准接口 - **方法**: `void execute(Long jobId, String param)` ### 4. JobDO (定时任务数据对象) - **位置**: `dal/dataobject/job/JobDO.java` - **字段**: - `id`: 任务ID - `name`: 任务名称 - `status`: 任务状态(1=正常, 2=暂停, 3=停止) - `handlerName`: 任务处理器名称 - `handlerParam`: 任务参数 - `cronExpression`: Cron表达式 ## ✅ 已完成功能 ### 1. 核心框架层 ✅ - [x] `JobHandler` - 任务处理器接口 - [x] `JobManager` - 任务管理器(基于Quartz) - [x] `InvokerJob` - 任务执行器 - [x] `QuartzConfig` - Quartz配置类 - [x] `JobInitRunner` - 启动时自动初始化任务 ### 2. 数据持久层 ✅ - [x] `JobDO` - 定时任务DO - [x] `JobLogDO` - 任务执行日志DO - [x] `JobMapper` - 任务Mapper - [x] `JobLogMapper` - 任务日志Mapper ### 3. 业务逻辑层 ✅ - [x] `JobService` - 任务Service接口 - [x] `JobServiceImpl` - 任务Service实现 - [x] `JobLogService` - 任务日志Service接口 - [x] `JobLogServiceImpl` - 任务日志Service实现 ### 4. 控制器层 ✅ - [x] `JobController` - 任务管理API - [x] VO类(SaveReqVO, PageReqVO, RespVO等) - [x] `JobConvert` - 对象转换器 ### 5. 数据库脚本 ✅ - [x] `infra_job` - 定时任务表 - [x] `infra_job_log` - 任务执行日志表 - [x] 示例任务数据 ## 📝 剩余工作清单 ### 1. 创建任务处理器示例 ⏳ - [ ] 在业务模块中创建 `AppointmentPushJobHandler` - [ ] 在业务模块中创建 `SyncTaskEquipmentJobHandler` - [ ] 在业务模块中创建 `EquipUpdateStatusJobHandler` - [ ] 其他业务JobHandler ### 2. 更新父POM ⏳ - [ ] 在 `pom.xml` 中添加 `tz-module-quartz` - [ ] 确保依赖关系正确 ### 3. Quartz集群表 ⏳ - [ ] 创建Quartz原生的11张集群表 - [ ] 配置集群参数 ## 🚀 使用流程 ### 步骤1: 在业务模块中创建JobHandler ```java // 在 tz-module-pressure 中创建 @Component("appointmentPushJobHandler") public class AppointmentPushJobHandler implements JobHandler { @Resource private AppointmentPushService appointmentPushService; @Override public void execute(Long jobId, String param) { log.info("执行约检推送任务, jobId: {}, param: {}", jobId, param); LocalDate checkDate = LocalDate.now(); if (StrUtil.isNotEmpty(param)) { checkDate = LocalDate.parse(param); } appointmentPushService.pushAppointmentConfirmData(checkDate); } } ``` ### 步骤2: 在Quartz服务中配置任务 ```java // 方式1: 通过API创建 POST /jobs/create { "name": "约检数据推送", "handlerName": "appointmentPushJobHandler", "handlerParam": "", "cronExpression": "0 0 2 * * ?", "status": 1 } // 方式2: 直接插入数据库 INSERT INTO infra_job (name, handler_name, handler_param, cron_expression, status) VALUES ('约检数据推送', 'appointmentPushJobHandler', '', '0 0 2 * * ?', 1); ``` ### 步骤3: 启动服务 ```bash # 启动 Quartz 服务 java -jar tz-module-quartz-biz.jar # 启动业务服务 java -jar tz-module-pressure-biz.jar ``` ## 📊 数据库表设计 ### infra_job (定时任务表) ```sql CREATE TABLE `infra_job` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务编号', `name` varchar(64) NOT NULL COMMENT '任务名称', `status` tinyint NOT NULL COMMENT '任务状态', `handler_name` varchar(64) NOT NULL COMMENT '任务处理器名称', `handler_param` varchar(255) DEFAULT NULL COMMENT '任务处理器参数', `cron_expression` varchar(128) NOT NULL COMMENT 'Cron表达式', `creator` varchar(64) DEFAULT NULL COMMENT '创建者', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` varchar(64) DEFAULT NULL COMMENT '更新者', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务表'; ``` ### infra_job_log (任务执行日志表) ```sql CREATE TABLE `infra_job_log` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志编号', `job_id` bigint NOT NULL COMMENT '任务编号', `handler_name` varchar(64) NOT NULL COMMENT '任务处理器名称', `handler_param` varchar(255) DEFAULT NULL COMMENT '任务处理器参数', `execute_index` tinyint NOT NULL DEFAULT '1' COMMENT '第几次执行', `begin_time` datetime NOT NULL COMMENT '开始时间', `duration` int DEFAULT NULL COMMENT '执行时长', `status` tinyint NOT NULL COMMENT '任务状态', `creator` varchar(64) DEFAULT NULL COMMENT '创建者', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_job_id` (`job_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务执行日志表'; ``` ## 🎁 优势对比 | 特性 | 分布式XXL-Job | 独立Quartz服务 | 业务模块内Quartz | |-----|--------------|---------------|----------------| | 避免重复执行 | ✅ 支持 | ✅ 支持 | ❌ 不支持 | | 管理界面 | ✅ 有 | ⚠️ 需开发 | ❌ 无 | | 调度可视化 | ✅ 有 | ⚠️ 需开发 | ❌ 无 | | 部署复杂度 | 中 | 低 | 低 | | 维护成本 | 低 | 中 | 低 | | 学习成本 | 低 | 中 | 低 | | 依赖外部服务 | 需要(调度中心) | 不需要 | 不需要 | ## 📚 参考文档 - [芋道源码 - Job](https://www.iocoder.cn/Spring-Boot/Job/) - [Quartz官方文档](http://www.quartz-scheduler.org/documentation/) - [Spring Boot集成Quartz](https://docs.spring.io/spring-boot/docs/current/reference/html/io.html) ## 💡 最佳实践 1. **任务Handler命名规范**: `{功能}JobHandler` - 示例: `appointmentPushJobHandler`, `syncTaskEquipmentJobHandler` 2. **参数传递**: 使用JSON格式的字符串 - 示例: `{"checkDate": "2023-01-01", "limit": 100}` 3. **异常处理**: JobHandler中必须捕获并处理异常 ```java try { // 业务逻辑 } catch (Exception e) { log.error("任务执行失败", e); // 不抛出异常,避免任务一直重试 } ``` 4. **幂等性**: 任务必须支持重复执行 - 使用数据库唯一索引 - 使用Redis分布式锁 - 记录已处理的数据ID 5. **监控告警**: - 记录任务执行日志 - 任务执行失败发送告警 - 任务执行超时告警 ## 🔗 相关链接 - 独立服务参考: `tz-module-mq` (消息队列服务) - 压力模块定时任务: `tz-module-pressure/job/*` - 数据迁移指南: `QUARTZ_MIGRATION_GUIDE.md` --- **状态**: 已完成基础架构,剩余Service实现、Controller和SQL脚本。