跳到主要内容

定时调度

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);
}
}
}
}