# 任务调度系统 **Repository Path**: brisklan/bs_tasks ## Basic Information - **Project Name**: 任务调度系统 - **Description**: 基于thinkphp8+workerman+redis的任务调度系统 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-28 - **Last Updated**: 2026-06-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 任务调度管理系统 基于 `ThinkPHP8 + Workerman + Redis Stream + MySQL + Vue2(ElementUI)` 的轻量任务调度系统。 适用场景: - HTTP 接口定时调度 - 秒级 / 分钟级 / 每日固定时间 / Cron 表达式任务 - 多 Worker 异步消费 - 执行日志留痕 - 失败恢复、超时恢复、Pending 恢复 - 后台可视化查看任务状态、执行记录、系统状态 ## 技术栈 - ThinkPHP 8 - Workerman - Redis Stream - MySQL 5.7+ - Vue2 + ElementUI ## 核心角色 当前任务系统由 3 个核心进程组成: 1. `Scheduler` 2. `Worker` 3. `Monitor` Linux 下推荐统一使用: - `php think TaskAll start` - `php think TaskAll start d` - `php think TaskAll status` Windows 下由于 Workerman 多进程限制,通常分开启动: - `php think TaskScheduler start` - `php think TaskWorker start` - `php think TaskMonitor start` ## 任务调度功能说明 这个项目的任务调度功能围绕“HTTP 定时触发”设计。后台负责配置任务,Scheduler 负责计算什么时候触发,Redis 负责排队和运行态协同,Worker 负责异步发起 HTTP 请求,MySQL 负责保存任务定义和执行日志。 ### 支持的调度规则 任务支持三类基础调度规则: - 间隔执行:支持秒、分钟、小时、天,例如每 `30` 秒、每 `5` 分钟。 - 固定时间:每天某个固定时间执行,例如每天 `03:00:00`。 - Cron 表达式:支持 5 位 Cron,例如 `0 3 * * *` 表示每天凌晨 3 点。 调度规则只决定“候选执行时间”。如果配置了执行时间窗口,系统会继续判断候选时间是否落在窗口内。 ### 执行时间窗口 任务可以再叠加时间窗口限制: - 一次性窗口:例如 `2028-01-01 03:00:00` 开始,`2028-01-01 10:00:00` 停止。 - 每天窗口:例如每天 `03:00:00` 到 `05:00:00` 执行。 - 每周窗口:例如每周一、周三 `03:00:00` 到 `05:00:00` 执行。 - 每月窗口:例如每月 `1` 号 `03:00:00` 到 `05:00:00` 执行。 开始/停止时间和循环窗口可以同时使用。开始/停止时间是任务的总有效期,循环窗口是有效期内允许执行的周期时段。 例如: ```text 调度类型:间隔执行 间隔:30 秒 循环窗口:每月 窗口日期:1号 窗口时段:03:00:00 至 05:00:00 ``` 实际执行时间为: ```text 每月1号 03:00:00 每月1号 03:00:30 ... 每月1号 04:59:30 ``` 窗口结束时间是排他边界,`05:00:00` 不执行。 ### 调度与入队 Scheduler 不直接请求业务接口,只做调度: 1. 启动时从 MySQL 导入启用任务。 2. 根据任务规则计算 `next_run_time`。 3. 将任务 ID 和到期时间写入 Redis 调度集合。 4. 定时扫描到期任务。 5. 到期后创建一条 `task_execute_log`,状态为 `排队中`。 6. 将执行消息写入 Redis Stream;Redis 低版本不支持 Stream 时降级为 List 队列。 7. 预先计算任务下一次执行时间。 这样 Scheduler 和 Worker 解耦:Scheduler 只关心“什么时候该执行”,Worker 只关心“拿到消息后怎么执行”。 ### 异步 HTTP Worker Worker 默认使用 `workerman/http-client` 异步 HTTP 客户端,不再按“取一条、等返回、再取下一条”的串行方式执行。 Worker 执行流程: 1. 从 Redis 队列批量读取消息。 2. 将日志状态改为 `执行中`。 3. 把 HTTP 请求交给 Workerman 事件循环。 4. Worker 主循环继续拉取其他任务。 5. HTTP 成功或失败回调触发时,更新执行日志和任务最近结果。 6. ACK 并删除 Redis 消息。 并发能力由两个参数共同决定: ```text 总 HTTP 并发约等于 TASK_WORKER_COUNT * TASK_WORKER_ASYNC_HTTP_CONCURRENCY ``` 例如: ```env TASK_WORKER_COUNT=2 TASK_WORKER_ASYNC_HTTP_CONCURRENCY=20 ``` 理论上最多同时发起约 `40` 个 HTTP 请求。 ### 超时与迟到响应 每个任务的 `timeout_seconds` 是单个 HTTP 请求的最大等待时间。 如果 `timeout_seconds=3`,请求超过 3 秒未返回,本系统会主动超时并记录失败。超时断开后,对方服务再返回响应,本系统已经收不到,不能再更新这条日志。 如果必须记录更晚返回的最终结果,有两种方式: - 将 `timeout_seconds` 设置得更长。 - 让对方系统完成后主动回调本系统。 如果只是要求“定时触发接口,对方快速返回 200 即算投递成功”,当前异步 HTTP 模式已经足够。 ### 重叠执行与单任务并发 任务支持控制同一个任务是否允许同时存在多次执行: - `allow_overlap=0`:不允许重叠。上一次还在排队或执行中,本次会被跳过或顺延。 - `allow_overlap=1`:允许重叠。适合高频任务或接口耗时不稳定的任务。 - `max_concurrency`:同一任务允许的 `排队中 + 执行中` 总数。 全局 Worker 并发控制系统总吞吐,单任务并发控制某一个任务不能把资源全部占满。 ### 执行日志 每次实际入队都会创建执行日志。日志状态含义: - `0`:排队中 - `1`:执行中 - `2`:成功 - `3`:失败 日志用于后台查看、统计成功率、统计耗时、异常恢复。调度器和 Worker 不会因为重建 Redis 缓存而删除历史执行日志。 ### Redis 的作用 Redis 在任务调度中承担四类职责: - 调度集合:保存任务的下次执行时间。 - 执行队列:保存待执行消息,优先使用 Redis Stream,低版本 Redis 降级为 List。 - 处理状态:保存 List 降级模式下的处理中消息,支持 Pending 恢复。 - Worker 心跳:记录 Worker 是否存活。 MySQL 保存长期数据,Redis 保存运行时状态。 ### 异常恢复 Monitor 负责修复异常状态: - Worker 崩溃导致消息未 ACK:重新 claim 或标记失败。 - 执行日志长时间停留在执行中:按超时恢复。 - Worker 心跳过期:回收运行状态。 - 任务长时间卡在排队中:自动恢复为可重新调度状态。 ## 任务流程总览 ```text 后台新增/修改任务 ↓ TaskService 保存任务 ↓ 计算 next_run_time ↓ 发布 schedule 通知到 Redis ↓ Scheduler 同步任务到调度集合 ↓ 到达执行时间后,写入 Redis Stream ↓ 生成一条 task_execute_log 排队记录 ↓ Worker 消费 Stream 消息 ↓ 异步发起 HTTP 请求 ↓ HTTP success/error 回调更新执行日志(success/fail) ↓ 更新任务最近结果、下次执行时间、运行状态 ↓ Monitor 持续巡检异常并恢复 ``` ## 流程图 ### 1. 主流程 ```text ┌──────────────┐ │ 后台任务管理 │ └──────┬───────┘ │ 新增 / 编辑 / 启停 / 删除 v ┌──────────────────────────────┐ │ TaskService │ │ - 保存任务 │ │ - 自动生成 task_code │ │ - 计算 next_run_time │ └──────┬───────────────────────┘ │ publishSchedule v ┌──────────────────────────────┐ │ Redis 调度通知 │ └──────┬───────────────────────┘ v ┌──────────────────────────────┐ │ Scheduler │ │ - rebuildSchedule │ │ - syncTaskToSchedule │ │ - dispatchDueScheduledTasks │ └──────┬───────────────────────┘ │ 到期后 enqueueTask v ┌──────────────────────────────┐ │ Redis Stream │ │ - 任务消息入队 │ └──────┬───────────────────────┘ │ 同时写入 ├──────────────► task_execute_log(status=0 排队中) v ┌──────────────────────────────┐ │ Worker │ │ - 读取 Stream │ │ - 标记执行中 │ │ - 异步调用 HTTP 任务 │ └──────┬───────────────────────┘ │ HTTP 回调 ├──────────────► task_execute_log(status=2/3) └──────────────► task 表最近结果 / 下次执行时间 / 运行状态 ``` ### 2. 异常恢复流程 ```text ┌──────────────┐ │ Monitor │ └──────┬───────┘ │ 每 10 秒巡检 v ┌──────────────────────────────────────┐ │ 检查 1:Pending 消息 │ │ - 长时间未确认 ACK │ │ - 重新 claim │ │ - 超过最大重试则标记失败 │ │ - 未超过则重新投递 │ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 检查 2:执行超时 │ │ - running 日志超时 │ │ - 标记失败 │ │ - 清理 worker_consumer │ │ - 重置任务运行态 │ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 检查 3:Worker 心跳 │ │ - 心跳过期视为死 Worker │ │ - 清理心跳 │ │ - 回收任务运行状态 │ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 检查 4:Scheduler 卡住的排队任务 │ │ - queue_status=1 但长时间未运行 │ │ - 自动恢复为可重新调度状态 │ └──────────────────────────────────────┘ ``` ## 各进程职责 ### 1. Scheduler 只负责调度,不负责执行。 主要动作: - 启动时全量重建调度集合 - 接收任务变更通知 - 将启用任务同步到 Redis 调度集合 - 扫描到期任务 - 将到期任务写入 Redis Stream - 预先计算下一次执行时间 - 恢复卡住的排队任务 对应代码: - `app/service/TaskProcessService.php` - `app/service/TaskDispatchService.php` ### 2. Worker 只负责消费和执行,不负责调度。 主要动作: - 从 Redis Stream 消费消息 - 写入消费者名称和开始执行时间 - 更新执行日志状态为“执行中” - 通过 `workerman/http-client` 异步发起 HTTP 请求 - 在 HTTP success/error 回调里将结果写回任务表和执行日志 - 请求完成后 ACK 并删除已处理消息 - 定时上报心跳 对应代码: - `app/service/TaskProcessService.php` - `app/service/TaskAsyncHttpWorkerService.php` - `app/service/TaskDispatchService.php` - `app/service/TaskService.php` ### 3. Monitor 只负责巡检和恢复。 主要动作: - 恢复 Pending 消息 - 处理执行超时 - 检测死 Worker - 清理异常运行状态 对应代码: - `app/service/TaskProcessService.php` - `app/service/TaskDispatchService.php` ## 数据流说明 ### 1. 任务表 `task` 保存任务定义和任务当前运行态,例如: - 任务名称 - 自动生成的任务编码 `task_code` - 请求地址、请求方式、请求头、请求参数 - 调度规则 - `next_run_time` - `queue_status` - `running_status` - `last_status` - `last_http_code` - `last_error` ### 2. 执行日志表 `task_execute_log` 每次入队或执行都会落一条记录,状态含义: - `0` 排队中 - `1` 执行中 - `2` 成功 - `3` 失败 用途: - 追踪每次执行 - 后台执行记录查看 - 统计成功率、失败率、执行耗时 - 恢复 Pending / Timeout / 死 Worker 时提供依据 ### 3. Redis 当前任务系统主要用到三类 Redis 数据: 1. 调度集合 - 保存任务和到期时间 - Scheduler 用它判断哪些任务到期 2. Redis Stream - 真正的执行队列 - Worker 从这里消费任务 3. Heartbeat - Worker 心跳 - Monitor 和后台统计用它判断 Worker 是否存活 ## 后台状态面板是怎么来的 首页统计面板主要来自以下数据源: - MySQL - 任务总数 - 启用 / 禁用数量 - 今日执行次数 - 今日成功 / 失败 - 当前排队 / 执行中数量 - 平均耗时 - Redis - `ping` - Redis 版本 - Stream / 心跳是否正常 - Worker 运行状态 - `TaskRuntimeStateService` 写入 `runtime` 状态文件 - Redis Heartbeat - 系统进程命令行辅助判断 状态判断逻辑不是只看进程名,而是综合: - runtime 状态文件最近写入时间 - Worker 心跳是否在有效期内 - 当前系统里是否存在对应命令进程 ## 执行时间窗口 任务除了原有的调度规则外,还支持配置执行时间窗口: - `start_at`:开始执行时间,空表示立即生效 - `end_at`:停止执行时间,空表示长期有效 - `time_window_type`:循环窗口类型,支持不循环、每天、每周、每月 - `window_days`:循环窗口日期,每周使用 `1-7` 表示周一到周日,每月使用 `1-31` 表示自然日 - `window_start_time` / `window_end_time`:循环窗口每天的起止时段 执行时间窗口不是一种新的调度类型,而是对现有调度规则的额外限制。系统仍然先按 `interval`、`daily` 或 `cron` 计算下一次执行时间,再判断这个时间是否落在窗口内。 ### 一次性窗口示例 例如需要从 `2028-01-01 03:00:00` 开始,每分钟执行一次,到 `2028-01-01 10:00:00` 停止: - 调度类型:间隔执行 - 间隔:`1` 分钟 - 开始执行时间:`2028-01-01 03:00:00` - 停止执行时间:`2028-01-01 10:00:00` 实际执行时间为: ```text 2028-01-01 03:00:00 2028-01-01 03:01:00 ... 2028-01-01 09:59:00 ``` 停止时间是排他边界,`2028-01-01 10:00:00` 不再执行。 ### 循环窗口示例 例如需要每月 `1` 号 `03:00:00` 到 `05:00:00` 之间,每 `30` 秒执行一次: - 调度类型:间隔执行 - 间隔:`30` 秒 - 循环窗口:每月 - 窗口日期:`1` 号 - 窗口时段:`03:00:00` 至 `05:00:00` 实际执行时间为: ```text 2028-01-01 03:00:00 2028-01-01 03:00:30 ... 2028-01-01 04:59:30 2028-02-01 03:00:00 ``` 窗口结束时间同样是排他边界,`05:00:00` 不执行。 ### 调度原理 - 当前时间早于 `start_at` 时,任务不会立即入队,`next_run_time` 会推迟到窗口开始后。 - 对间隔任务,开始前的下一次执行时间直接设为 `start_at`。 - 对每日固定时间和 Cron 任务,系统从 `start_at` 附近开始计算第一个符合规则的时间。 - 配置循环窗口时,系统会先找到当前或下一段有效窗口,再在窗口内按原调度规则计算执行点。 - 如果当前窗口内已经没有下一次执行时间,系统会跳到下一段循环窗口继续计算。 - 如果计算出的下一次执行时间大于等于 `end_at`,系统认为窗口已结束,`next_run_time` 写为 `NULL`,并从 Redis 调度集合移除。 - 如果某次任务已经到期,例如 `09:59:00`,即使它执行后没有下一次,也会正常写入执行日志并入队执行;执行完成后不再安排后续调度。 ### 数据与日志 执行窗口只影响任务表的调度字段: - `task.start_at` - `task.end_at` - `task.time_window_type` - `task.window_days` - `task.window_start_time` - `task.window_end_time` - `task.next_run_time` 它不会删除 `task_execute_log` 的历史记录。每次实际入队仍然会写执行日志,用于后台记录、统计和异常恢复。 ## 1 秒任务为什么能支持 为了兼容高频任务,当前系统已做两类处理: ### 1. 更高频轮询 通过环境变量控制: ```env TASK_SCHEDULER_POLL_SECONDS=0.2 TASK_WORKER_POLL_SECONDS=0.2 TASK_WORKER_BLOCK_MS=200 TASK_WORKER_USE_ASYNC_HTTP=true TASK_WORKER_ASYNC_HTTP_CONCURRENCY=20 TASK_WORKER_CONNECT_TIMEOUT=3 TASK_WORKER_MAX_CONN_PER_ADDR=20 TASK_WORKER_KEEPALIVE_TIMEOUT=15 TASK_WORKER_MAX_RESPONSE_BYTES=5000 ``` 说明: - Scheduler 每 `0.2s` 扫描一次到期任务 - Worker 每 `0.2s` 拉取一次消息 - Stream 阻塞读取时间为 `200ms` - 默认启用 Workerman `http-client` 异步 HTTP Worker - `TASK_WORKER_ASYNC_HTTP_CONCURRENCY` 表示每个 Worker 进程内最多同时执行多少个 HTTP 请求 - 总并发约等于 `TASK_WORKER_COUNT * TASK_WORKER_ASYNC_HTTP_CONCURRENCY` - `TASK_WORKER_MAX_CONN_PER_ADDR` 表示同一个目标域名/IP 的最大连接池数量 - 同一任务要支持并发 2 个及以上,必须保证 `TASK_WORKER_COUNT` 也足够 - `TASK_WORKER_CONNECT_TIMEOUT` 只控制连接阶段超时 - 单个任务的 `timeout_seconds` 控制整个 HTTP 请求的最大等待时间 ### 2. 可选允许重叠执行 任务支持 `allow_overlap`: - `0` 不允许重叠执行 - `1` 允许重叠执行 同时支持 `max_concurrency`: - 默认 `1` - 表示同一任务同时允许存在的 `排队中 + 执行中` 总数 - 只有开启“允许重叠执行”后,这个值才会大于 `1` 生效 当任务频率很高,例如 `1 秒一次`,如果上一次还没执行完,且又不允许重叠,就会被拦住。 当开启异步并发后,如果某个任务达到自己的并发上限,系统不会继续为这个任务新增排队日志,而是直接顺延到下一次调度时间,避免执行记录里长期堆积大量 `排队中`。 所以高频任务建议: - 适当缩短任务本身执行时长 - 必要时开启“允许重叠执行” ### 3. Workerman 异步 HTTP Worker 定时 Worker 默认使用 `workerman/http-client` 异步执行 HTTP 请求。旧链路是“取一条队列消息、等待 HTTP 返回、再取下一条”,如果每个接口耗时 `3` 秒,几百个任务会明显排队。 异步 Worker 的链路是: ```text Worker 主循环 -> 批量读取 Redis 队列消息 -> 将多个 HTTP 请求交给 Workerman 事件循环 -> HTTP 成功或失败回调里立即写执行日志 -> ack/delete 对应 Redis 消息 -> 释放并发槽位继续取新任务 ``` 如果 `TASK_WORKER_COUNT=2`、`TASK_WORKER_ASYNC_HTTP_CONCURRENCY=20`,理论上最多同时执行约 `40` 个 HTTP 请求。 需要注意:如果任务的 `timeout_seconds=3`,请求超过 `3` 秒仍未返回,HTTP 客户端会主动超时并记录失败。超时后对方服务再返回的响应,本系统无法再接收,也就不能再更新这条日志。要记录超过超时后的最终结果,只能把 `timeout_seconds` 设置得更长,或改成业务回调模式。 ## 运行链路对应代码 ### 后台任务管理 - `app/admin/view/task/index.html` - `app/result/admin/TaskResult.php` - `app/service/TaskService.php` ### 执行记录 - `app/admin/controller/TaskLog.php` - `app/result/admin/TaskLogResult.php` - `app/admin/view/task_log/index.html` ### 调度与执行 - `app/service/TaskDispatchService.php` - `app/service/TaskProcessService.php` - `app/service/TaskAsyncHttpWorkerService.php` - `app/service/TaskStreamService.php` - `app/service/TaskRuntimeStateService.php` ### 首页统计 - `app/admin/controller/Index.php` - `app/admin/view/index/dashboard.html` ### 命令入口 - `app/command/TaskAll.php` - `app/command/TaskScheduler.php` - `app/command/TaskWorker.php` - `app/command/TaskMonitor.php` ## 一句话理解整个流程 ```text 任务配置保存在 MySQL,Scheduler 负责“什么时候入队”,Redis Stream 负责“排队”,Worker 负责“真正执行”,Monitor 负责“异常恢复”,task_execute_log 负责“每次执行留痕”,后台面板负责“状态可视化”。 ``` ## Linux 资源监控 Linux 下如果使用 `TaskAll` 守护启动,进程名通常会显示为: ```text WorkerMan: master process WorkerMan: worker process ``` 所以不要只用 `grep TaskAll` 判断进程是否存在,建议直接看 Workerman 主进程和子进程。 ### 1. 查看任务进程是否在运行 ```bash ps -ef | grep WorkerMan ps -ef | grep think pgrep -a -f php ``` 如果能看到类似下面这条,说明任务系统已经启动: ```text WorkerMan: master process start_file=/www/wwwroot/your_project/think ``` ### 2. 查看主进程和子进程资源 假设主进程 PID 是 `14273`: ```bash ps -o pid,ppid,user,cmd,%cpu,%mem,rss,vsz -p 14273 --ppid 14273 ``` 示例输出: ```text PID PPID USER CMD %CPU %MEM RSS VSZ 14273 25615 www WorkerMan: master process 0.0 0.9 36548 563592 14283 14273 www WorkerMan: worker process 0.7 0.7 28640 567688 14284 14273 www WorkerMan: worker process 0.5 0.8 31504 568912 14285 14273 www WorkerMan: worker process 0.5 0.8 30896 567944 14286 14273 www WorkerMan: worker process 0.0 0.5 22632 567688 ``` 说明: - 第一行一般是 `master` - 后面几行是 `scheduler / worker / worker / monitor` - `RSS` 是实际占用内存,单位 `KB` - `%CPU` 看 CPU 占用 - `%MEM` 看内存占比 ### 3. 实时刷新查看 ```bash watch -n 1 "ps -o pid,ppid,user,cmd,%cpu,%mem,rss,vsz -p 14273 --ppid 14273" ``` 或者: ```bash top -H -p 14273 ``` ### 4. 查看进程树 ```bash pstree -ap 14273 ``` 可以用来确认当前到底拉起了几个子进程。 ### 5. 如果没有 `pidstat` 有些 CentOS 默认没装: ```bash pidstat -rud -p 1 ``` 如果提示 `command not found`,安装: ```bash yum install -y sysstat ``` ## 资源判断经验 ### 1. 当前这套系统的典型资源结构 通常会有这些进程: - 1 个 `master` - 1 个 `scheduler` - `N` 个 `task worker` - 1 个 `monitor` 例如 `TASK_WORKER_COUNT=2` 时,通常总共 5 个进程。 ### 2. 2核4G 的经验值 根据当前线上实测,单个进程常见内存大概在: - `master`:`30MB ~ 40MB` - `scheduler`:`20MB ~ 35MB` - `worker`:`25MB ~ 35MB / 个` - `monitor`:`20MB ~ 30MB` 所以 `TASK_WORKER_COUNT=2` 时,整套任务系统常见总内存大概: - `120MB ~ 180MB` 这个占用对 `2核4G` 机器来说是比较轻的。 ### 3. 吞吐量粗算 当前系统是“多 Worker 进程 + 单 Worker 内异步 HTTP 并发”模型。 粗算公式: ```text 最大同时请求数 ≈ TASK_WORKER_COUNT * TASK_WORKER_ASYNC_HTTP_CONCURRENCY 每秒完成数 ≈ 最大同时请求数 / 单次平均耗时(秒) ``` 示例: - `TASK_WORKER_COUNT=2` - `TASK_WORKER_ASYNC_HTTP_CONCURRENCY=20` - 最大同时请求数约 `40` 如果平均耗时: - `100ms`:理论约 `400 次/秒` - `500ms`:理论约 `80 次/秒` - `1s`:理论约 `40 次/秒` - `3s`:理论约 `13 次/秒` 实际值会比理论值低,因为还要扣除: - Redis 读写 - MySQL 写日志 - PHP 进程调度 - Workerman 事件循环调度 - 目标 HTTP 接口波动 ## 什么时候说明机器扛不住了 出现以下现象时,说明该优化或扩容: - `worker` 进程 `%CPU` 持续偏高 - `worker` 的 `RSS` 持续上涨且不回落 - 后台统计面板里 `排队中` 持续增长 - 后台统计面板里 `执行中` 长时间接近 `TASK_WORKER_COUNT * TASK_WORKER_ASYNC_HTTP_CONCURRENCY` - 1 秒任务开始明显堆积 - 执行日志里超时和失败开始变多 ## 优化建议 - 先保持 `TASK_WORKER_COUNT=2` - 逐步调整 `TASK_WORKER_ASYNC_HTTP_CONCURRENCY`,例如 `20 -> 50` - 如果任务目标域名集中,适当提高 `TASK_WORKER_MAX_CONN_PER_ADDR` - 如果任务多数是慢请求,不要盲目加太大,先观察目标接口是否扛得住 - 高频任务尽量缩短接口耗时 - 高频慢任务建议开启 `allow_overlap` - 定期观察执行记录、排队数、平均耗时、失败率