定时调度
ZHub 提供了强大的定时调度功能,支持基于 Cron 表达式和简单时间间隔的定时任务。
功能特性
- Cron 表达式: 支持标准的 Cron 表达式语法
- 简单间隔: 支持秒、分、时、天等时间单位
- 数据库持久化: 支持将定时任务配置保存到数据库
- 任务执行: 支持定时任务执行
- 动态重载: 支持运行时重新加载定时任务配置
基础使用
1. 简单定时任务
// 订阅定时任务
zhub.timer("T:A", () -> {
System.out.println("收到定时调度事件:T:A");
// 执行定时任务逻辑
});
// 每5秒执行一次
zhub.timer("T:B", () -> {
System.out.println("每5秒执行一次");
});
2. 基于 Cron 表达式的定时任务
// 每天早上8点执行
zhub.timer("daily-report", () -> {
System.out.println("生成日报");
});
// 每周一上午9点执行
zhub.timer("weekly-summary", () -> {
System.out.println("生成周报");
});
时间表达式语法
1. 简单时间间隔
ZHub 支持以下时间单位格式:
// 支持的时间单位
"5s" // 5秒
"10m" // 10分钟
"2H" // 2小时
"1d" // 1天
"1M" // 1个月
"1y" // 1年
// 纯数字表示毫秒
"5000" // 5000毫秒
2. Cron 表达式
ZHub 支持标准 Cron 表达式格式:
// 标准 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 表达式
// 工作日每天9点和18点执行
"0 9,18 * * 1-5"
// 每月1号和15号执行
"0 0 1,15 * *"
// 每小时的0分和30分执行
"0,30 * * * *"
// 每5分钟执行
"*/5 * * * *"
数据库配置
1. 数据库表结构
ZHub 定时任务数据库表结构:
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. 配置示例
-- 插入定时任务配置
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
中配置数据库连接:
[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. 单实例执行
// 单实例执行:只有一个客户端实例会执行任务
zhub.timer("single-task", () -> {
System.out.println("只有一个实例执行此任务");
});
2. 多实例执行
// 多实例执行:所有客户端实例都会执行任务
zhub.timer("multi-task", () -> {
System.out.println("所有实例都会执行此任务");
});
3. 动态重载配置
# 通过管理接口重新加载定时任务配置
curl http://127.0.0.1:711/timer/reload
4. 本地定时任务
ZHub 客户端还提供本地定时任务功能:
// 本地延时任务(适用于短时间延时)
Timers.delay(() -> {
System.out.println("延时5秒后执行");
}, 5000);
// 本地重试任务
Timers.tryDelay(() -> {
// 返回 true 表示成功,false 表示需要重试
return processTask();
}, 1000, 3); // 每1秒重试一次,最多重试3次
实际应用场景
1. 数据同步任务
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. 监控和告警
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. 缓存刷新
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. 任务命名规范
// 推荐:使用有意义的任务名称
zhub.timer("user:data-sync", () -> {});
zhub.timer("order:status-check", () -> {});
zhub.timer("system:health-check", () -> {});
// 避免:使用无意义的名称
zhub.timer("task1", () -> {});
zhub.timer("timer", () -> {});
2. 异常处理
zhub.timer("safe-task", () -> {
try {
// 任务逻辑
processTask();
} catch (Exception e) {
logger.error("定时任务执行失败", e);
// 发送告警或记录错误
}
});
3. 性能优化
// 避免在定时任务中执行耗时操作
zhub.timer("light-task", () -> {
// 快速执行的任务
updateStatus();
});
// 耗时操作应该异步执行
zhub.timer("heavy-task", () -> {
CompletableFuture.runAsync(() -> {
// 异步执行耗时操作
processHeavyTask();
});
});
4. 监控和日志
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)
- 时间表达式格式错误
解决方法:
# 检查数据库连接
curl http://127.0.0.1:711/_/info
# 重新加载配置
curl http://127.0.0.1:711/timer/reload
# 检查任务状态
SELECT * FROM tasktimer WHERE status = 10;
2. 任务重复执行
可能原因:
- 多个客户端实例都订阅了同一个任务
- 单实例配置错误
解决方法:
-- 确保单实例配置正确
UPDATE tasktimer SET single = 1 WHERE name = 'task-name';
3. 任务执行时间不准确
可能原因:
- 系统时间不同步
- 任务执行时间过长
解决方法:
- 同步系统时间
- 优化任务执行逻辑
- 考虑拆分长时间任务
监控和调试
1. 任务执行状态
# 查看所有定时任务状态
curl http://127.0.0.1:711/_/info | jq '.timersize'
2. 日志监控
// 在任务中添加详细日志
zhub.timer("logged-task", () -> {
logger.info("定时任务开始执行: {}", LocalDateTime.now());
try {
processTask();
logger.info("定时任务执行完成: {}", LocalDateTime.now());
} catch (Exception e) {
logger.error("定时任务执行异常: {}", e.getMessage(), e);
}
});
3. 性能监控
// 监控任务执行时间
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);
}
}
}
}