--- sidebar_position: 2 title: 定时调度 description: ZHub 定时调度功能详解 --- # 定时调度 ZHub 提供了强大的定时调度功能,支持基于 Cron 表达式和简单时间间隔的定时任务。 --- ## 功能特性 - **Cron 表达式**: 支持标准的 Cron 表达式语法 - **简单间隔**: 支持秒、分、时、天等时间单位 - **数据库持久化**: 支持将定时任务配置保存到数据库 - **任务执行**: 支持定时任务执行 - **动态重载**: 支持运行时重新加载定时任务配置 --- ## 基础使用 ### 1. 简单定时任务 ```java // 订阅定时任务 zhub.timer("T:A", () -> { System.out.println("收到定时调度事件:T:A"); // 执行定时任务逻辑 }); // 每5秒执行一次 zhub.timer("T:B", () -> { System.out.println("每5秒执行一次"); }); ``` ### 2. 基于 Cron 表达式的定时任务 ```java // 每天早上8点执行 zhub.timer("daily-report", () -> { System.out.println("生成日报"); }); // 每周一上午9点执行 zhub.timer("weekly-summary", () -> { System.out.println("生成周报"); }); ``` --- ## 时间表达式语法 ### 1. 简单时间间隔 ZHub 支持以下时间单位格式: ```java // 支持的时间单位 "5s" // 5秒 "10m" // 10分钟 "2H" // 2小时 "1d" // 1天 "1M" // 1个月 "1y" // 1年 // 纯数字表示毫秒 "5000" // 5000毫秒 ``` ### 2. Cron 表达式 ZHub 支持标准 Cron 表达式格式: ```java // 标准 Cron 表达式格式:分 时 日 月 周 "0 8 * * *" // 每天8点 "0 0 1 * *" // 每月1号0点 "0 9 * * 1" // 每周一9点 "*/5 * * * *" // 每5分钟 "0 0 0 1 1" // 每年1月1日0点 ``` ### 3. 复杂 Cron 表达式 ```java // 工作日每天9点和18点执行 "0 9,18 * * 1-5" // 每月1号和15号执行 "0 0 1,15 * *" // 每小时的0分和30分执行 "0,30 * * * *" // 每5分钟执行 "*/5 * * * *" ``` --- ## 数据库配置 ### 1. 数据库表结构 ZHub 定时任务数据库表结构: ```sql CREATE TABLE `tasktimer` ( `timerid` varchar(64) NOT NULL DEFAULT '' COMMENT '[主键]UUID', `name` varchar(32) NOT NULL DEFAULT '' COMMENT '[任务名称]', `expr` varchar(32) NOT NULL DEFAULT '' COMMENT '[时间表达式]', `single` int NOT NULL DEFAULT '1' COMMENT '[单实例消费]1单对象,0不限', `remark` varchar(128) NOT NULL DEFAULT '' COMMENT '[备注]', `status` smallint NOT NULL DEFAULT '10' COMMENT '[状态]10启用,60停用', PRIMARY KEY (`timerid`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` ### 2. 配置示例 ```sql -- 插入定时任务配置 INSERT INTO `tasktimer` (`timerid`, `name`, `expr`, `single`, `remark`, `status`) VALUES ('T1', 'T:A', '*/5 * * * * ?', 1, '每5秒执行一次', 10), ('T2', 'T:B', '15s', 1, '每15秒执行一次', 10), ('T3', 'T:C', '0 0 0 * * 1', 0, '每周一00:00执行', 10), ('T4', 'T:D', '0 0 24 * * ?', 1, '每天00:00执行', 10); ``` ### 3. 服务端配置 在 `app.ini` 中配置数据库连接: ```ini [ztimer] db.addr=127.0.0.1:3306 db.user=root db.password=123456 db.database=zhub db.schema=public # PostgreSQL 模式名 db.type=mysql # mysql|postgres ``` --- ## 高级功能 ### 1. 单实例执行 ```java // 单实例执行:只有一个客户端实例会执行任务 zhub.timer("single-task", () -> { System.out.println("只有一个实例执行此任务"); }); ``` ### 2. 多实例执行 ```java // 多实例执行:所有客户端实例都会执行任务 zhub.timer("multi-task", () -> { System.out.println("所有实例都会执行此任务"); }); ``` ### 3. 动态重载配置 ```bash # 通过管理接口重新加载定时任务配置 curl http://127.0.0.1:711/timer/reload ``` ### 4. 本地定时任务 ZHub 客户端还提供本地定时任务功能: ```java // 本地延时任务(适用于短时间延时) Timers.delay(() -> { System.out.println("延时5秒后执行"); }, 5000); // 本地重试任务 Timers.tryDelay(() -> { // 返回 true 表示成功,false 表示需要重试 return processTask(); }, 1000, 3); // 每1秒重试一次,最多重试3次 ``` --- ## 实际应用场景 ### 1. 数据同步任务 ```java public class DataSyncService { private final ZHubClient zhub; public void init() { // 每小时同步一次数据 zhub.timer("data-sync", () -> { syncData(); }); // 每天凌晨2点清理过期数据 zhub.timer("data-cleanup", () -> { cleanupExpiredData(); }); } private void syncData() { System.out.println("开始同步数据..."); // 数据同步逻辑 } private void cleanupExpiredData() { System.out.println("开始清理过期数据..."); // 数据清理逻辑 } } ``` ### 2. 监控和告警 ```java public class MonitorService { private final ZHubClient zhub; public void init() { // 每30秒检查系统状态 zhub.timer("system-check", () -> { checkSystemHealth(); }); // 每天生成监控报告 zhub.timer("daily-report", () -> { generateDailyReport(); }); } private void checkSystemHealth() { // 系统健康检查逻辑 if (isSystemHealthy()) { System.out.println("系统状态正常"); } else { sendAlert("系统异常,请检查"); } } } ``` ### 3. 缓存刷新 ```java public class CacheService { private final ZHubClient zhub; public void init() { // 每5分钟刷新缓存 zhub.timer("cache-refresh", () -> { refreshCache(); }); // 每天凌晨3点预热缓存 zhub.timer("cache-warmup", () -> { warmupCache(); }); } private void refreshCache() { System.out.println("刷新缓存..."); // 缓存刷新逻辑 } } ``` --- ## 最佳实践 ### 1. 任务命名规范 ```java // 推荐:使用有意义的任务名称 zhub.timer("user:data-sync", () -> {}); zhub.timer("order:status-check", () -> {}); zhub.timer("system:health-check", () -> {}); // 避免:使用无意义的名称 zhub.timer("task1", () -> {}); zhub.timer("timer", () -> {}); ``` ### 2. 异常处理 ```java zhub.timer("safe-task", () -> { try { // 任务逻辑 processTask(); } catch (Exception e) { logger.error("定时任务执行失败", e); // 发送告警或记录错误 } }); ``` ### 3. 性能优化 ```java // 避免在定时任务中执行耗时操作 zhub.timer("light-task", () -> { // 快速执行的任务 updateStatus(); }); // 耗时操作应该异步执行 zhub.timer("heavy-task", () -> { CompletableFuture.runAsync(() -> { // 异步执行耗时操作 processHeavyTask(); }); }); ``` ### 4. 监控和日志 ```java public class MonitoredTimer { private final ZHubClient zhub; private final Logger logger = LoggerFactory.getLogger(MonitoredTimer.class); public void init() { zhub.timer("monitored-task", () -> { long startTime = System.currentTimeMillis(); try { processTask(); long duration = System.currentTimeMillis() - startTime; logger.info("定时任务执行成功,耗时: {}ms", duration); } catch (Exception e) { long duration = System.currentTimeMillis() - startTime; logger.error("定时任务执行失败,耗时: {}ms", duration, e); } }); } } ``` --- ## 故障排除 ### 1. 任务不执行 **可能原因**: - 数据库连接配置错误 - 任务状态为停用(status=60) - 时间表达式格式错误 **解决方法**: ```bash # 检查数据库连接 curl http://127.0.0.1:711/_/info # 重新加载配置 curl http://127.0.0.1:711/timer/reload # 检查任务状态 SELECT * FROM tasktimer WHERE status = 10; ``` ### 2. 任务重复执行 **可能原因**: - 多个客户端实例都订阅了同一个任务 - 单实例配置错误 **解决方法**: ```sql -- 确保单实例配置正确 UPDATE tasktimer SET single = 1 WHERE name = 'task-name'; ``` ### 3. 任务执行时间不准确 **可能原因**: - 系统时间不同步 - 任务执行时间过长 **解决方法**: - 同步系统时间 - 优化任务执行逻辑 - 考虑拆分长时间任务 --- ## 监控和调试 ### 1. 任务执行状态 ```bash # 查看所有定时任务状态 curl http://127.0.0.1:711/_/info | jq '.timersize' ``` ### 2. 日志监控 ```java // 在任务中添加详细日志 zhub.timer("logged-task", () -> { logger.info("定时任务开始执行: {}", LocalDateTime.now()); try { processTask(); logger.info("定时任务执行完成: {}", LocalDateTime.now()); } catch (Exception e) { logger.error("定时任务执行异常: {}", e.getMessage(), e); } }); ``` ### 3. 性能监控 ```java // 监控任务执行时间 public class PerformanceMonitor { public void monitorTask(String taskName, Runnable task) { long startTime = System.currentTimeMillis(); try { task.run(); } finally { long duration = System.currentTimeMillis() - startTime; if (duration > 5000) { // 超过5秒记录警告 logger.warn("任务 {} 执行时间过长: {}ms", taskName, duration); } } } } ```