Files
ulthon_admin/.agents/skills/ulthon-timer/SKILL.md
2026-03-26 20:22:34 +08:00

141 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: "ulthon-timer"
description: "内置秒级定时器php think timer的使用与扩展规范用于新增/调整定时任务site/call、并发分片、TimerController 防刷、timer.mode normal/parallel。"
---
# timer内置秒级定时器
## 核心机制(你要记住的 3 件事)
1) 任务配置统一从 `app_file_path('common/command/timer/config.php')` 读取
2) 每个配置会按 `concurrency` 自动展开成多份任务实例,并自动注入 `concurrency_id` / `concurrency_count`
3) “是否该执行”主要由定时器侧的 Cache 节流控制(可选叠加控制器侧的防刷保护)
相关实现入口:
- 系统入口(优先从 app 层理解):
- 定时器命令入口:`app/common/command/Timer.php`(实现继承自 `extend/base/common/command/TimerBase.php`
- 任务实例服务入口:`app/common/service/TimerService.php`(实现继承自 `extend/base/common/service/TimerServiceBase.php`
- site 任务控制器基类入口:`app/common/controller/TimerController.php`(实现继承自 `extend/base/common/controller/TimerControllerBase.php`
- 实现文件(需要深入机制时再看 Base
- [Timer.php](../../../app/common/command/Timer.php) / [TimerBase.php](../../../extend/base/common/command/TimerBase.php)
- [TimerService.php](../../../app/common/service/TimerService.php) / [TimerServiceBase.php](../../../extend/base/common/service/TimerServiceBase.php)
- [TimerController.php](../../../app/common/controller/TimerController.php) / [TimerControllerBase.php](../../../extend/base/common/controller/TimerControllerBase.php)
- 运行模式配置:[timer.php](../../../config/timer.php)
## 新增定时任务(默认规则)
无特殊情况下,新增定时任务应当通过本机制实现:在 `timer/config.php` 注册任务 + 以 `site``call` 的方式实现目标逻辑。
### 1) 选择任务类型
- `site`:通过 HTTP 访问一个控制器地址(默认优先使用)。即使任务需要“长时间运行”,也尽量设计成 `site` 模式(分片/分批/可重入),以复用框架的页面/接口同体、鉴权、日志、事务、限流等能力。
- `call`:直接执行一个 PHP callable不推荐除非万不得已。仅在确实不适合走 HTTP 上下文、且不希望暴露为控制器入口时使用。
长时间运行任务的推荐写法(仍用 `site`
- 设计为“可重入”的短任务:每次 `do()` 只处理一小批数据,处理进度写入缓存/表,下次继续
- 结合 `concurrency` 做分片:按 `$this->concurrencyId` 划分数据范围,多个实例并行推进
- 结合 `frequency` 控制节奏:用调度频率限制整体吞吐,避免单次占用过久
### 2) 编写任务目标target
#### A. site 类型HTTP 任务)
实现方式建议:
- 框架使用者(做业务):仅在 `app/tools/controller/timer/` 新增控制器 `*.php` 即可;不需要、也不应该在 `extend/base/` 增加 `*Base.php`
- 框架作者(维护内核):在 `extend/base/tools/controller/timer/` 新增 `*Base.php` 作为默认实现,同时在 `app/tools/controller/timer/` 增加同名入口类 `*.php` 继承 Base系统唯一调用入口
- 控制器建议继承 `app\common\controller\TimerController`(以获得并发参数校验与可选的 `$frequency` 防刷);执行入口一般提供 `do()`
示例(已有实现可参考):[ClearLog.php](../../../app/tools/controller/timer/ClearLog.php)、[ClearLogBase.php](../../../extend/base/tools/controller/timer/ClearLogBase.php)
并发与防刷建议:
- 如需并发分片处理,在控制器中设置:
- `protected $concurrency = N;`
- 使用 `$this->concurrencyId` 做分片编号0 ~ N-1
- 如希望防止外部重复请求(不仅是定时器自身节流),在控制器中设置:
- `protected $frequency = 秒数;`
- 这会启用 `TimerControllerBase::protectVisit()` 基于 URL 的防刷限制
#### B. call 类型(函数任务)
目标形态:
- `target``call_user_func` 可执行的 callable例如`[SomeService::class, 'method']`、闭包等
- 由于 Base/App 双层机制的入口要求,类引用应指向 `app/` 下的入口类(由入口类继承 Base 实现)
- 长时间/重任务不建议用 `call`:它缺少 HTTP 上下文与控制器层通用能力,排错与复用成本更高
### 3) 注册到任务配置timer/config.php
默认配置文件位置(支持分层覆盖):
- 优先覆盖:`app/common/command/timer/config.php`(一旦存在,`app_file_path(...)` 将优先读取此文件)
- 框架默认:`extend/base/common/command/timer/config.php`(当 app 未提供时回落到该文件)
字段说明(兜底默认值由 Base 层的 `initConfigItem()` 提供):
- `name`:任务唯一名称(用于 Cache key不可重复
- `type``site``call`
- `target`
- `site`:以 `/` 开头的相对路径(建议指向 `tools/timer.*` 控制器的 `do` 方法)
- `call`callable
- `frequency`:执行频率(秒),小于 0 会被修正为 0
- `concurrency`:并发数量(默认 1`site` 类型会自动把并发参数写入 query
- `run_type`:保留字段(当前未参与调度逻辑)
示例site
```php
return [
[
'name' => 'clear_log',
'type' => 'site',
'target' => '/tools/timer.ClearLog/do',
'frequency' => 600,
'concurrency' => 1,
],
];
```
示例call
```php
return [
[
'name' => 'system_host_register',
'type' => 'call',
'target' => [\app\common\service\HostService::class, 'heartbeat'],
'frequency' => 30,
],
];
```
## 运行与验证
### 运行命令
- 常规运行:`php think timer`
- 只跑一轮(便于验证):`php think timer --temp`
- 无任务时不输出“no request”`php think timer --quit`
### 本地调试(指定请求 Host
site 任务会按站点域名发起请求,默认从 `sysconfig('site','site_domain')` 读取。
- 本地调试:`php think timer --local --local-host=http://localhost --local-port=8000`
### 运行模式
配置在 [timer.php](../../../config/timer.php)
- `normal`:单进程循环 + Guzzle async默认
- `parallel`Workerman 多进程模式(并发更高,相关连接参数在 `timer.php` 中)
## 常见坑位(快速自检)
- `name` 重复:会导致 Cache key 冲突,表现为任务“莫名其妙不跑/跑得不对”
- `concurrency` 与控制器侧 `$concurrency` 不一致:会触发 `concurrency id/count error`
- 只依赖控制器侧 `$frequency`:它只是防刷,不是调度;调度频率以定时器侧 Cache 节流为准