diff --git a/.agents/skills/ulthon-timer/SKILL.md b/.agents/skills/ulthon-timer/SKILL.md index 45db964..b54cceb 100644 --- a/.agents/skills/ulthon-timer/SKILL.md +++ b/.agents/skills/ulthon-timer/SKILL.md @@ -34,10 +34,74 @@ description: "内置秒级定时器(php think timer)的使用与扩展规范 长时间运行任务的推荐写法(仍用 `site`): -- 设计为“可重入”的短任务:每次 `do()` 只处理一小批数据,处理进度写入缓存/表,下次继续 +- 设计为"可重入"的短任务:每次 `do()` 只处理一小批数据,处理进度写入缓存/表,下次继续 - 结合 `concurrency` 做分片:按 `$this->concurrencyId` 划分数据范围,多个实例并行推进 - 结合 `frequency` 控制节奏:用调度频率限制整体吞吐,避免单次占用过久 +#### 按时间窗口循环处理(队列消费推荐) + +适用于:需要持续消费队列/轮询数据的场景(如消息处理、订单状态同步、通知推送等)。 + +核心思路:在 `do()` 方法内设置一个**最大执行时间窗口**,循环处理单条数据,超时后自动退出,由定时器下次调度继续。 + +```php +class MyQueueTask extends TimerController +{ + // 每次执行的最大运行时间(秒),根据业务和定时器 frequency 合理设置 + // 建议不超过 frequency 的一半,留出调度间隔 + protected $maxRunTime = 5; + + // 防刷间隔(秒),与定时器侧 frequency 配合 + protected $frequency = 10; + + public function do() + { + $maxTime = time() + $this->maxRunTime; + + do { + $this->doItem(); + + // 超时检查:时间窗口用完则退出,下次调度继续 + if (time() >= $maxTime) { + break; + } + } while (true); + } + + protected function doItem() + { + // 取一条待处理的数据 + $item = SomeModel::where('status', 0)->order('id', 'asc')->find(); + + if (empty($item)) { + // 队列为空时短暂休眠,避免空转消耗 CPU + sleep(1); + return; + } + + // ... 处理单条数据的业务逻辑 ... + + $item->status = 1; + $item->save(); + } +} +``` + +**要点:** + +| 配置项 | 建议 | +|--------|------| +| `maxRunTime` | 不超过 `frequency` 的一半;例如 `frequency=10` 时设 3~5 秒 | +| `doItem()` 中的 `sleep()` | 队列空时必须休眠,防止 CPU 空转;有数据时不要 sleep | +| `doItem()` 的粒度 | 每次只处理一条/一批数据,保证可重入、可中断 | +| `frequency` | 与 `maxRunTime` 配合,`frequency` >= `maxRunTime * 2` 为宜 | + +**与普通定时任务的区别:** + +- 普通任务:`do()` 执行一次就返回,靠定时器周期性调度 +- 时间窗口模式:`do()` 在时间窗口内持续循环消费,处理完积压数据后自动退出 +- 优势:面对突发积压数据时,单次调度可处理多条,提高吞吐;超时安全退出,不会阻塞定时器 + ### 2) 编写任务目标(target) #### A. site 类型(HTTP 任务)