# task-scheduler **Repository Path**: yzli_dev/task-scheduler ## Basic Information - **Project Name**: task-scheduler - **Description**: No description available - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-14 - **Last Updated**: 2026-06-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Task Scheduler - Go语言任务调度工具 一个功能强大的任务调度工具,支持依赖关系调度和定时调度,可以执行shell脚本、Python脚本和系统命令。支持 YAML 配置文件和 MySQL 数据库两种配置源,支持 Docker 容器化部署。 ## 功能特性 - ✅ **多种任务类型**: 支持Shell脚本、Python脚本和系统命令 - ✅ **依赖关系调度**: 基于DAG(有向无环图)的任务依赖管理 - ✅ **定时调度**: 支持Cron表达式进行定时任务调度 - ✅ **Web 仪表盘**: 可视化监控面板,实时展示任务状态、DAG 依赖图和执行记录 - ✅ **双配置源**: 支持 YAML 文件和 MySQL 数据库两种配置方式 - ✅ **状态持久化**: MySQL 模式下任务执行状态自动保存到数据库,重启后断点续调度 - ✅ **重试机制**: 任务失败自动重试 - ✅ **超时控制**: 可配置任务超时时间 - ✅ **环境变量**: 支持为任务设置环境变量 - ✅ **日志记录**: 详细的任务执行日志和统计信息,支持配置全局日志文件(`log_file`)和日志级别(`log_level`) - ✅ **脚本主动上报**: 脚本可通过 HTTP API 主动上报运行状态和进度,调度器自身判断仍为最终结果 - ✅ **跨平台**: 支持 Windows / Linux / macOS ## 项目结构 ``` task-scheduler/ ├── cmd/ # 主程序入口 │ ├── main.go │ ├── console_windows.go # Windows 控制台编码设置 │ └── console_other.go # 其他平台控制台编码 ├── config/ # 配置管理 │ ├── config.go │ ├── tasks.yaml # 任务配置文件示例 │ ├── db.yaml # MySQL 数据库配置 │ └── db-docker.yaml # Docker 环境数据库配置 ├── database/ # 数据库相关 │ ├── mysql.go # MySQL 连接初始化 │ ├── task_repository.go # 任务数据访问层 │ ├── state_store.go # 状态持久化存储 │ └── schema.sql # 数据库表结构 ├── executor/ # 任务执行器 │ └── executor.go ├── models/ # 数据模型 │ └── task.go ├── reporter/ # 脚本状态上报 │ ├── reporter.go # 上报处理器 │ └── server.go # 独立上报服务器 ├── scheduler/ # 调度器 │ ├── dag_scheduler.go # DAG调度器 │ └── cron_scheduler.go # Cron定时调度器 ├── utils/ # 工具类 │ ├── task_manager.go # 任务管理器 │ └── logger.go # 全局日志器 ├── web/ # Web 仪表盘 │ ├── server.go # HTTP 服务器 & SSE 推送 │ ├── executor_wrapper.go# 带状态追踪的执行器 │ └── static/ │ └── index.html # 前端仪表盘页面 ├── examples/ # 示例脚本 │ ├── backup.sh # Shell 示例 │ ├── backup.bat # Windows Batch 示例 │ ├── backup_with_report.sh # 带状态上报的 Shell 示例 │ ├── backup_with_report.bat # 带状态上报的 Batch 示例 │ ├── backup_with_report.py # 带状态上报的 Python 示例 │ └── process_data.py # Python 示例 ├── logs/ # 日志目录 ├── Dockerfile # Docker 多阶段构建 ├── .dockerignore # Docker 构建忽略文件 ├── go.mod └── README.md ``` ## 安装 ### 前置要求 - Go 1.25+ - Python 3.x(如果需要执行Python脚本) - MySQL 8.0+(如果使用数据库配置源) ### 安装步骤 ```bash # 克隆或进入项目目录 cd task-scheduler # 初始化Go模块(如果还没有) go mod init task-scheduler # 下载所有依赖 go mod tidy # 编译 go build -o task-scheduler ./cmd/ ``` ## 使用方法 ### 配置源选择 本工具支持两种配置源: | 配置源 | 适用场景 | 配置文件 | |--------|----------|----------| | YAML | 简单部署、少量任务 | `config/tasks.yaml` | | MySQL | 集中管理、大量任务、动态更新 | `config/db.yaml` | ### 方式一:YAML 配置 #### 1. 配置任务 编辑 `config/tasks.yaml` 文件,定义您的任务: ```yaml tasks: - id: task_example name: "示例任务" type: shell command: "echo 'Hello World'" timeout: 60 retries: 3 enabled: true ``` #### 2. 运行 ```bash # 立即执行 ./task-scheduler run --config config/tasks.yaml # 定时调度 ./task-scheduler start --config config/tasks.yaml # Web 仪表盘 ./task-scheduler web --config config/tasks.yaml --port 8080 ``` ### 方式二:MySQL 配置 #### 1. 启动 MySQL 使用 Docker 快速启动: ```bash # 拉取镜像 docker pull mysql:8.0 # 启动容器 docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root123 \ -v ~/mysql-data:/var/lib/mysql \ --restart unless-stopped \ mysql:8.0 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci ``` #### 2. 初始化数据库 ```bash # 创建数据库 docker exec -it mysql8 mysql -uroot -proot123 \ -e "CREATE DATABASE task_scheduler CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 导入表结构和示例数据(注意指定字符集) docker exec -i mysql8 mysql -uroot -proot123 \ --default-character-set=utf8mb4 task_scheduler < database/schema.sql ``` #### 3. 配置连接 编辑 `config/db.yaml`: ```yaml host: "127.0.0.1" port: 3306 user: "root" password: "root123" dbname: "task_scheduler" charset: "utf8mb4" # 连接池配置 max_open_conns: 10 max_idle_conns: 5 ``` #### 4. 运行 ```bash # 立即执行 ./task-scheduler run --source mysql --db-config config/db.yaml # 定时调度 ./task-scheduler start --source mysql --db-config config/db.yaml # Web 仪表盘 ./task-scheduler web --source mysql --db-config config/db.yaml --port 8080 ``` ### 运行模式 #### 模式一:立即执行(DAG调度) 一次性执行所有任务,按照依赖关系顺序: ```bash ./task-scheduler run --source yaml # 或 mysql ``` #### 模式二:定时调度(Cron调度) 启动定时调度器,按照cron表达式定期执行: ```bash ./task-scheduler start --source yaml # 或 mysql ``` 停止调度器(Ctrl+C) #### 模式三:Web 仪表盘 启动 Web 可视化仪表盘,同时运行调度器并通过浏览器实时监控任务状态: ```bash # 默认端口 8080 ./task-scheduler web --source yaml # 或 mysql # 自定义端口 ./task-scheduler web --source mysql --port 3000 ``` 启动后打开浏览器访问 `http://localhost:8080`,即可看到: - **状态概览** — 任务总数、运行中、成功、失败等统计卡片 - **DAG 依赖图** — 任务间的依赖关系可视化,节点颜色反映当前状态 - **任务列表** — 所有任务详情,支持按状态筛选 - **执行记录** — 最近执行历史,点击查看输出和错误日志 非定时任务会立即以 DAG 模式执行,定时任务由 Cron 调度器持续运行,所有状态变化实时推送到前端。 ### 状态持久化与断点续调度(MySQL 模式) 在 `--source mysql` 模式下,任务执行状态会自动保存到数据库。如果程序被中断(Ctrl+C、崩溃等),下次启动时会自动恢复: - 已成功的任务会被跳过,不再重复执行 - 中断时正在执行的任务会被标记为失败 - 程序创建新的运行批次,从断点继续调度 ```bash # 第一次运行:执行了 task1、task2,在 task3 时中断 ./task-scheduler run --source mysql # 第二次运行:自动跳过 task1、task2,从 task3 开始继续 ./task-scheduler run --source mysql ``` > **注意**:YAML 模式下状态仅保存在内存中,重启后不会恢复。 ### Docker 部署 项目支持 Docker 容器化部署,应用和 MySQL 分别运行在独立容器中,通过 Docker 网络连接。 #### 前置准备 ```bash # 创建 Docker 网络 docker network create task-net # 启动 MySQL 容器(设置时区避免时间偏差) docker run -d --name mysql8 --network task-net \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root123 \ -e TZ=Asia/Shanghai \ -v ~/mysql-data:/var/lib/mysql \ --restart unless-stopped \ mysql:8.0 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci \ --default-time-zone='+08:00' # 初始化数据库 docker exec -it mysql8 mysql -uroot -proot123 \ -e "CREATE DATABASE task_scheduler CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" docker exec -i mysql8 mysql -uroot -proot123 \ --default-character-set=utf8mb4 task_scheduler < database/schema.sql ``` #### 构建镜像 ```bash # 构建 Docker 镜像(多阶段构建,自动处理 Go 模块代理) docker build -t task-scheduler:latest . ``` > Dockerfile 已配置国内 Go 模块代理(goproxy.cn),国内环境无需额外配置。 #### 运行容器 ```bash # 启动应用容器(使用 Docker 专用数据库配置) docker run -d --name task-scheduler --network task-net \ -p 8080:8080 \ task-scheduler:latest \ web --source mysql --db-config config/db-docker.yaml --port 8080 ``` `config/db-docker.yaml` 中数据库 host 设为 `mysql8`(容器名),通过 Docker 网络解析。 #### 查看日志 ```bash docker logs -f task-scheduler ``` #### 国内拉取基础镜像 如果 Docker Hub 无法访问,可使用镜像源: ```bash docker pull docker.1ms.run/golang:1.25-alpine docker tag docker.1ms.run/golang:1.25-alpine golang:1.25-alpine docker pull docker.1ms.run/alpine:3.20 docker tag docker.1ms.run/alpine:3.20 alpine:3.20 ``` ### 命令行参数 ```bash ./task-scheduler --help 用法: task-scheduler [command] 可用命令: run 立即执行所有任务(按依赖关系) start 启动定时调度器 web 启动 Web 仪表盘(可视化调度状态) help 显示帮助信息 标志: --config string 配置文件路径,source=yaml 时使用 (默认 "config/tasks.yaml") --db-config string 数据库配置文件路径,source=mysql 时使用 (默认 "config/db.yaml") --source string 配置源: yaml 或 mysql (默认 "yaml") --log-dir string 日志目录 (默认 "logs") --port int Web 仪表盘端口 (默认 8080) --help 显示帮助信息 ``` ## 配置说明 ### 全局配置字段 YAML 配置文件顶部可设置全局参数: | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | log_level | string | `info` | 日志级别:`debug` / `info` / `warn` / `error` | | log_file | string | `scheduler.log` | 全局日志文件路径,设为空或 `stdout` 则仅输出到控制台 | | max_workers | int | `10` | 最大并发工作数(预留) | | task_timeout | int | `300` | 默认任务超时时间(秒),单个任务可覆盖 | | retry_delay | int | `5` | 重试间隔(秒) | 示例: ```yaml log_level: info log_file: scheduler.log task_timeout: 300 tasks: - id: my_task ... ``` 启动后日志会同时写入 `scheduler.log` 和控制台,记录调度启动、任务执行开始/成功/失败/跳过等事件。 ### 任务配置字段 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | id | string | 是 | 任务唯一标识 | | name | string | 否 | 任务名称(默认使用id) | | type | string | 否 | 任务类型:shell/python/command(默认shell) | | command | string | 是 | 执行的命令或脚本路径 | | args | []string | 否 | 命令参数列表 | | work_dir | string | 否 | 工作目录 | | env | map[string]string | 否 | 环境变量 | | timeout | int | 否 | 超时时间(秒) | | retries | int | 否 | 重试次数(默认1) | | dependencies | []string | 否 | 依赖的任务ID列表 | | cron | string | 否 | Cron表达式(定时任务) | | enabled | bool | 否 | 是否启用(默认true) | ### Cron表达式格式 标准5位Cron表达式:`分 时 日 月 周` 示例: - `*/5 * * * *` - 每5分钟执行 - `0 2 * * *` - 每天凌晨2点执行 - `0 0 * * 0` - 每周日午夜执行 - `0 0 1 * *` - 每月1号午夜执行 ### MySQL 表结构 数据库包含四张表: **tasks 表** - 存储任务定义 | 字段 | 类型 | 说明 | |------|------|------| | id | VARCHAR(64) | 任务唯一标识(主键) | | name | VARCHAR(128) | 任务名称 | | type | ENUM | 任务类型:shell/python/command | | command | TEXT | 命令或脚本路径 | | args | JSON | 命令参数列表 | | work_dir | VARCHAR(255) | 工作目录 | | env | JSON | 环境变量 | | timeout | INT | 超时时间(秒) | | retries | INT | 重试次数 | | cron | VARCHAR(64) | Cron表达式 | | enabled | TINYINT(1) | 是否启用 | | created_at | DATETIME | 创建时间 | | updated_at | DATETIME | 更新时间 | **task_dependencies 表** - 存储任务依赖关系 | 字段 | 类型 | 说明 | |------|------|------| | task_id | VARCHAR(64) | 任务ID(外键) | | dependency_id | VARCHAR(64) | 依赖的任务ID(外键) | **task_runs 表** - 存储调度运行批次(状态持久化) | 字段 | 类型 | 说明 | |------|------|------| | id | VARCHAR(36) | 运行批次ID(UUID,主键) | | run_type | ENUM | 运行类型:dag/cron | | status | ENUM | 运行状态:running/completed/failed/interrupted | | started_at | DATETIME | 开始时间 | | finished_at | DATETIME | 结束时间 | | created_at | DATETIME | 创建时间 | **task_executions 表** - 存储任务执行记录(状态持久化) | 字段 | 类型 | 说明 | |------|------|------| | id | VARCHAR(36) | 执行记录ID(UUID,主键) | | run_id | VARCHAR(36) | 所属运行批次(外键) | | task_id | VARCHAR(64) | 任务ID(外键) | | status | ENUM | 执行状态:pending/running/success/failed/skipped | | start_time | DATETIME | 开始时间 | | end_time | DATETIME | 结束时间 | | duration_ms | INT | 耗时(毫秒) | | output | LONGTEXT | 标准输出 | | error | TEXT | 错误信息 | | exit_code | INT | 退出码 | | retry_count | INT | 已重试次数 | | created_at | DATETIME | 创建时间 | 唯一约束:`(run_id, task_id)` — 同一批次中每个任务只有一条记录(UPSERT)。 ## 示例 ### Shell脚本任务 ```yaml - id: backup_db name: "数据库备份" type: shell command: "mysqldump -u root mydb > backup.sql" timeout: 300 retries: 2 cron: "0 2 * * *" ``` ### Python脚本任务 ```yaml - id: data_processing name: "数据处理" type: python command: "/opt/scripts/process.py" args: - "--input" - "data.csv" - "--output" - "result.csv" work_dir: "/opt/scripts" env: PYTHONPATH: "/opt/lib" timeout: 600 dependencies: - backup_db ``` ### 带依赖的任务链 ```yaml tasks: - id: step1 name: "步骤1" type: shell command: "echo 'Step 1'" - id: step2 name: "步骤2" type: shell command: "echo 'Step 2'" dependencies: - step1 - id: step3 name: "步骤3" type: shell command: "echo 'Step 3'" dependencies: - step2 ``` ## 日志 任务执行日志保存在 `logs/` 目录下,每个任务有一个独立的日志文件: ``` logs/ ├── task_cleanup.log ├── task_data_process.log └── task_backup.log ``` 日志格式为JSON,包含: - 任务ID - 执行状态 - 开始/结束时间 - 执行时长 - 退出码 - 输出内容 - 错误信息 ## 脚本主动上报 脚本可以在执行过程中通过 HTTP POST 主动向调度器上报自身运行状态和进度。调度器基于退出码判断最终结果的机制完全保留,脚本上报作为补充信息用于更精细的实时监控。 ### 工作原理 调度器在启动时会自动开启一个上报端点,并在执行每个脚本时注入以下环境变量: | 环境变量 | 说明 | |---------|------| | `CALLBACK_URL` | 上报接口的完整 URL | | `TASK_ID` | 当前任务的 ID | 脚本只需检查 `CALLBACK_URL` 是否存在,存在则可通过 HTTP POST 上报状态,不存在则静默跳过——完全向后兼容,无需修改现有脚本。 ### 上报接口 ``` POST {CALLBACK_URL} Content-Type: application/json { "task_id": "backup_db", // 必填,任务ID "status": "running", // 必填: running / success / failed "progress": 50, // 可选: 进度百分比 0-100 "message": "正在备份数据库...", // 可选: 进度描述 "output": "...", // 可选: 输出内容 "error": "..." // 可选: 错误信息 } ``` 响应示例: ```json { "ok": true, "message": "状态已接收" } ``` ### 各运行模式的支持 | 模式 | 上报地址 | 说明 | |------|---------|------| | `web` | `http://localhost:{port}/api/report` | 复用 Web 服务器端口,上报状态实时推送到仪表盘 | | `run` / `start` | `http://localhost:{随机端口}/api/report` | 自动分配空闲端口,启动时日志输出具体地址 | ### 示例:Python 脚本上报 ```python import os, json from urllib import request def report(status, progress=0, message=""): url = os.environ.get("CALLBACK_URL", "") task_id = os.environ.get("TASK_ID", "") if not url or not task_id: return data = json.dumps({ "task_id": task_id, "status": status, "progress": progress, "message": message }).encode() try: request.urlopen(request.Request(url, data=data, headers={"Content-Type": "application/json"}), timeout=5) except Exception: pass report("running", 30, "正在处理数据...") # ... 执行业务逻辑 ... report("running", 80, "正在保存结果...") # ... 保存结果 ... report("success", 100, "处理完成") ``` ### 示例:Shell 脚本上报 ```bash report_status() { [ -z "$CALLBACK_URL" ] || [ -z "$TASK_ID" ] && return curl -s -X POST "$CALLBACK_URL" \ -H "Content-Type: application/json" \ -d "{\"task_id\":\"$TASK_ID\",\"status\":\"$1\",\"progress\":$2,\"message\":\"$3\"}" \ > /dev/null 2>&1 || true } report_status "running" 50 "正在备份..." # ... 执行备份 ... report_status "success" 100 "备份完成" ``` 完整的示例脚本见 `examples/` 目录下的 `backup_with_report.py`、`backup_with_report.sh`、`backup_with_report.bat`。 ## 开发 ### 添加新功能 1. 在 `models/task.go` 中定义数据模型 2. 在 `executor/executor.go` 中实现执行逻辑 3. 在 `scheduler/` 中实现调度策略 4. 在 `web/` 中扩展仪表盘功能 5. 在 `cmd/main.go` 中添加命令行接口 ### 测试 ```bash # 运行测试 go test ./... # 运行特定包的测试 go test ./executor/... ``` ## 常见问题 ### Q: 如何调试任务? A: 查看 `logs/` 目录下的日志文件,或使用 `--log-level debug` 参数。 ### Q: 任务执行失败怎么办? A: 检查日志文件中的错误信息,确认命令路径、权限和环境变量是否正确。 ### Q: 如何停止正在运行的任务? A: 按 Ctrl+C 可以优雅地停止调度器,正在执行的任务会完成当前执行后停止。 ### Q: Web 仪表盘无法访问? A: 确认防火墙未阻止对应端口,默认端口为 8080,可通过 `--port` 参数修改。如果端口被占用,换一个端口即可。 ### Q: 支持Windows吗? A: 支持。项目已针对 Windows 做了兼容(Shell 命令通过 `cmd.exe /c` 执行,信号处理使用 `os.Interrupt`,控制台已设置 UTF-8 编码)。 ### Q: Windows 下任务执行报错 "cannot run executable found relative to current directory"? A: 这是 Go 1.19+ 的安全机制。请确保项目根目录下没有残留的 `cmd.exe` 文件(可能是之前编译产生的),删除后即可正常运行。 ### Q: MySQL 中文显示乱码? A: 导入 schema.sql 时必须指定字符集: ```bash docker exec -i mysql8 mysql -uroot -proot123 --default-character-set=utf8mb4 task_scheduler < database/schema.sql ``` ### Q: 如何在 YAML 和 MySQL 之间切换? A: 使用 `--source` 参数: ```bash # YAML 模式 ./task-scheduler run --source yaml # MySQL 模式 ./task-scheduler run --source mysql ``` ## 许可证 MIT License ## 贡献 欢迎提交Issue和Pull Request! ## 联系方式 如有问题或建议,请提交Issue。 --- **当前版本**: v1.5.0