ThinkPHP用Redis实现优先级队列需绕过think-queue默认List模式,推荐ZSET(score排序)或多list+blpop分级消费;前者灵活支持动态调权,后者更稳定高效。

ThinkPHP 用 Redis 实现优先级队列,得绕过 think-queue 默认的 List 模式
ThinkPHP 官方 think-queue 扩展默认基于 Redis 的 LPUSH/RPOP(或 BRPOP)实现 FIFO 队列,它本身不支持任务优先级字段或 score 排序。直接调用 \think\Queue::push() 发送的任务,最终都进同一个 list 键(如 queue:default),无法按优先级取出。所以想加优先级,必须跳过默认推送逻辑,改用 Redis 原生命令 + 自定义消费控制。
用 Redis ZSET 存任务,zAdd 的 score 决定执行顺序
ZSET 是 ThinkPHP 支持且最贴近“优先级语义”的结构:score 越小越先被取到(升序),适合把高优任务设为低分值。常见做法是:score = priority × 1000000 - time(),这样同优先级下时间早的任务排前面。
- 推任务时不走
\think\Queue::push(),改用Redis::zAdd('queue:pri', $score, json_encode($payload)) - 消费端不用
queue:work,写个自定义命令(如php think queue:pri-consume),循环执行zRange('queue:pri', 0, 0, ['WITHSCORES' => true])取最小 score 任务 - 取到后立刻
zRem('queue:pri', $payload),避免重复消费;失败时可zAdd回去并调高 score(降权重试) - 注意:ZSET 不支持原生阻塞等待,需用
sleep(1)或结合zCard+ 短轮询,否则 CPU 空转
多 list 分级 + blpop 键顺序控制消费优先级
比 ZSET 更轻量、更符合 ThinkPHP 原有习惯的做法:预设多个 Redis list,如 queue:high、queue:default、queue:low,然后用 blpop 按顺序尝试弹出——只要第一个非空就处理,空了才往下走。
- 发任务时手动选择键:
Redis::lPush('queue:high', json_encode($data)),而不是依赖onQueue() - 消费脚本里调用
Redis::blPop(['queue:high', 'queue:default', 'queue:low'], 0),返回格式为['queue:high', $payload] - 这个命令天然阻塞且按数组顺序检查,无需轮询或 sleep,资源效率高
- 风险点:如果
queue:high长期无任务,blPop会一直等下去,但这是预期行为;若想防饿死,可设超时(如1秒),再手动 fallback 到下一队列
别在 config/queue.php 里硬配 queue 名来“假装”优先级
有人试图在配置里写 'queue' => 'high',再用 onQueue('high') 启动 queue:work --queue=high,default,这在 think-queue Redis 驱动下**无效**——它只是把所有任务都塞进 queue:high 这个 list,不会自动跨 list 调度,--queue 参数在这里只影响键名前缀,不改变消费逻辑。
立即学习“PHP免费学习笔记(深入)”;
真正起作用的是你消费时用的 Redis 命令。ZSET 或多 list + blpop 是目前 ThinkPHP 生态里最可控、上线成本最低的两种路径。ZSET 更灵活(支持动态调整 score),多 list 更稳定(命令语义清晰、调试方便)。选哪个,取决于你是否需要运行时修改单个任务的优先级。



















