mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-07-01 15:32:48 +08:00
6.6 KiB
6.6 KiB
name, description
| name | description |
|---|---|
| ulthon-timer | 内置秒级定时器(php think timer)的使用与扩展规范;用于新增/调整定时任务(site/call、并发分片、TimerController 防刷、timer.mode normal/parallel)。 |
timer(内置秒级定时器)
核心机制(你要记住的 3 件事)
- 任务配置统一从
app_file_path('common/command/timer/config.php')读取 - 每个配置会按
concurrency自动展开成多份任务实例,并自动注入concurrency_id/concurrency_count - “是否该执行”主要由定时器侧的 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/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、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或calltarget:site:以/开头的相对路径(建议指向tools/timer.*控制器的do方法)call:callable
frequency:执行频率(秒),小于 0 会被修正为 0concurrency:并发数量(默认 1)。site类型会自动把并发参数写入 queryrun_type:保留字段(当前未参与调度逻辑)
示例(site):
return [
[
'name' => 'clear_log',
'type' => 'site',
'target' => '/tools/timer.ClearLog/do',
'frequency' => 600,
'concurrency' => 1,
],
];
示例(call):
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:
normal:单进程循环 + Guzzle async(默认)parallel:Workerman 多进程模式(并发更高,相关连接参数在timer.php中)
常见坑位(快速自检)
name重复:会导致 Cache key 冲突,表现为任务“莫名其妙不跑/跑得不对”concurrency与控制器侧$concurrency不一致:会触发concurrency id/count error- 只依赖控制器侧
$frequency:它只是防刷,不是调度;调度频率以定时器侧 Cache 节流为准