在高并发场景下,防止 ThinkPHP 队列雪崩的核心是控制消费速度而非单纯增加生产者,建议优先使用 Redis 驱动配合 Supervisor 守护进程,并设置合理的单次处理数量与超时时间。
先说结论:队列雪崩通常是因为生产速度远大于消费速度导致资源耗尽,配置重点在于限制并发消费数和设置失败重试机制。
- 适合:使用 Redis 驱动处理耗时任务,避免同步阻塞请求
- 先准备:安装 Supervisor 守护进程工具,确认 Redis 服务稳定
- 验收:观察队列长度不再持续堆积,且 Worker 进程内存稳定
命令速用版
如果你需要快速启动队列监听,可以使用 ThinkPHP 自带的命令,但生产环境建议配合守护进程:
php think queue:listen `--queue`=your_queue_name若使用 Supervisor 管理,配置命令如下:
command=php /path/to/think queue:work `--queue`=your_queue_name `--tries`=3为什么会这样
队列雪崩的本质是“产消不平衡”。当突发流量导致大量任务进入队列,而消费者(Worker)处理速度跟不上时,队列长度会急剧增加。ThinkPHP 默认的队列配置通常偏向开发环境,直接用于高并发生产环境容易忽略超时控制和重试策略。
分步处理
1. 修改队列配置文件
打开 config/queue.php,确保驱动选择 redis 而非 database。补充以下关键配置项:
return [
'default' => 'redis',
'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'expire' => 60, // 任务超时时间,防止重复消费
'max_attempts' => 3, // 最大重试次数
],
],
];2. 配置 Supervisor 守护进程
创建配置文件 /etc/supervisor/conf.d/think_queue.conf,参考以下完整模板:
[program:think_queue]
command=php /www/wwwroot/your_project/think queue:work `--queue`=your_queue_name `--max-jobs`=1000
process_name=%(program_name)s_%(process_num)02d
numprocs=4
autostart=true
autorestart=true
user=www
redirect_stderr=true
stdout_logfile=/www/wwwlogs/queue_%(process_num)02d.log注意设置 `--max-jobs` 防止内存泄漏,numprocs 根据 CPU 核心数调整。
3. 代码层增加限流与幂等逻辑
在任务产生端,检测队列长度超过阈值时拒绝写入:
// 生产者示例
use think\facade\Redis;
public function produceTask($data)
{
$queueName = 'queues:your_queue_name';
$maxLen = 5000; // 阈值
// 检查队列长度
$currentLen = Redis::command('LLEN', [$queueName]);
if ($currentLen > $maxLen) {
throw new \Exception('队列堆积严重,暂停生产');
}
// 写入队列
\think\facade\Queue::push('JobClass', $data);
}在任务消费端,确保业务逻辑支持幂等,并包裹异常捕获:
// 消费者 Job 类示例
class JobClass
{
public function fire($job, $data)
{
try {
// 幂等性检查示例:利用唯一键防止重复处理
$uniqueKey = 'processed:' . $data['id'];
if (cache($uniqueKey)) {
$job->delete(); // 已处理过,直接删除
return;
}
// 业务逻辑
$this->doSomething($data);
// 标记已处理
cache($uniqueKey, 1, 3600);
$job->delete();
} catch (\Exception $e) {
// 记录日志但不抛出,避免进程退出
log_error($e->getMessage());
// 根据重试次数决定是否释放任务
if ($job->attempts() > 3) {
$job->delete();
}
}
}
}怎么验证是否生效
1. 检查队列长度
使用 Redis 客户端执行 LLEN 命令查看队列长度,观察在流量高峰期间数值是否稳定:
redis-cli LLEN queues:your_queue_name2. 监控 Worker 状态
使用 supervisorctl status 查看进程是否频繁重启。
3. 查看日志
检查 runtime/log 下的日志,确认是否有大量的 QueueFailed 或超时错误。
常见坑
1. 内存泄漏
PHP 长进程模式下,建议在 Worker 配置中设置 `--max-jobs` 处理一定数量任务后自动重启。
2. 重复消费
如果任务处理时间超过了 expire 设置,Redis 可能会将任务重新放回队列。确保业务逻辑支持幂等。
3. 异常捕获缺失
任务代码中必须包裹 try-catch,否则单个任务报错可能导致整个 Worker 进程退出。
参考来源
- ThinkPHP 官方文档 - 队列服务,URL: https://www.thinkphp.cn/doc/
- topthink/think-queue GitHub 仓库,URL: https://github.com/topthink/think-queue