444 lines
9.4 KiB
Markdown
444 lines
9.4 KiB
Markdown
---
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
``` |