不能直接用 Swoole\Timer::tick,必须通过 think-swoole 提供的「Tick 任务」机制注册;需在命令类中调用 Server::getInstance()->tick(5000, callback),且仅在服务启动后生效。

ThinkPHP + think-swoole 能不能直接用 Swoole\Timer::tick?
不能。think-swoole 封装了 Swoole 的 Server 生命周期,但没暴露原生 Swoole\Timer::tick 接口,硬调会报错或被忽略——因为 tick 必须在 Worker 进程中启动,而 think-swoole 的初始化时机和进程模型不保证你写的代码落在正确上下文里。
真正可用的方式是通过 think-swoole 提供的「Tick 任务」机制,它内部做了进程判断、协程兼容和生命周期绑定。
- 必须在
app/Command.php或自定义命令类中注册,不能写在控制器或中间件里 - 注册时机要在
onWorkerStart之后(think-swoole 自动处理,你只需确保命令被加载) - 回调函数内不能用同步阻塞操作(如
file_get_contents、sleep),否则卡死整个 Worker
怎么注册一个每 5 秒执行一次的 Tick 任务?
用 think-swoole 内置的 tick 方法,不是 Swoole 原生的。它挂载在 SwooleServer 实例上,需通过 think\swoole\Server 获取。
推荐做法:新建命令类 app/command/TickTask.php,内容如下:
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\swoole\Server;
class TickTask extends Command
{
protected function configure()
{
$this->setName('swoole:tick')->setDescription('Register tick task');
}
protected function execute(Input $input, Output $output)
{
Server::getInstance()->tick(5000, function () {
// 注意:这里运行在 Worker 进程,可安全访问 Db、Cache 等
\think\facade\Log::info('Tick executed at ' . date('Y-m-d H:i:s'));
});
$output->writeln('Tick registered.');
}
}
-
5000是毫秒,不是秒;填5不会等 5 秒,而是 5 毫秒,极大概率触发失败或高频抖动 - 回调里不要 return 任何值,返回值会被忽略;有状态要存 Redis 或数据库
- 该命令只需执行一次(如部署时
php think swoole:tick),不是每次请求都跑
为什么定时任务没执行?常见掉坑点
最常遇到的是「注册了但没反应」,基本都卡在这几个环节:
- 没启动 think-swoole 的
http或websocket服务,只跑php think swoole:tick不生效——tick 依赖 Server 实例存活 - 用
php think swoole:start --daemon启动后,忘记检查日志:runtime/swoole.log里搜tick或Timer,看是否报SWOOLE_ERR - 回调里用了
$this或闭包绑定了外部对象(如 Controller 实例),会导致内存泄漏甚至崩溃——tick 回调应是无状态纯函数 - 开发环境开了 Xdebug,Swoole 协程下 Xdebug 可能干扰定时器精度,建议关闭后再测试
Tick 任务和 Laravel 的 Scheduler 有什么本质区别?
不是「能不能替代」的问题,是运行模型完全不同:Laravel Scheduler 依赖外部 cron 进程拉起 PHP CLI,每次都是新进程;think-swoole 的 tick 是长驻 Worker 进程里的内存级循环,毫秒级精度、零启动开销,但也意味着一旦 Worker 挂了,tick 就停了,且无法跨机器分发。
- 适合做实时性要求高的事:比如每秒刷新在线人数缓存、心跳检测、连接池健康检查
- 不适合做耗时任务:单次执行超过 100ms 就可能挤压其他请求处理,得拆成协程异步(
go)或投递到 TaskWorker - 没有「错过补偿」逻辑:如果某次 tick 因阻塞延迟了,下一次不会叠加执行,就是单纯跳过
真正难的不是写那几行注册代码,是想清楚这个 tick 是不是真的需要常驻内存、有没有更轻量的替代方案(比如 Redis key 过期监听 + publish/subscribe)。很多人一上来就上 tick,结果发现只是想每天凌晨清缓存——那还是老老实实用系统 cron 更稳。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










