应选delayed模式(基于zset),因其支持延迟、高并发不丢任务、可支撑1000+ qps;list + blpop仅适用于简单场景,不具备延迟能力且重试和可靠性差。

用 Redis 实现可靠的 PHP 异步任务队列,关键不在“能跑”,而在“不丢、可重试、能延时、扛高并发”。简单用 rpush + blpop 只适合本地调试或低频脚本,生产环境必须升级设计。
选对数据结构:优先用 delayed 模式(zset),别碰 list + blpop
Redis 原生 List 配合 blpop 缺乏延迟能力,重试逻辑全靠代码兜底,高并发下容易漏任务、重复消费、连接干扰。真正可靠的选择是基于有序集合(zset)的 delayed 模式——它把执行时间作为 score 存入 zset,用 ZRANGEBYSCORE 扫描到期任务,天然支持延迟、去重、按序执行。
前提条件:
- Redis 版本 ≥ 6.2(需要
ZMSCORE命令支持原子更新) - Webman 项目中启用
plugin/webman/redis-queue插件,并在config/queue.php显式设置'delayed' => true - 避免与普通 list 队列共用同一 Redis 连接池,防止
ZADD和BLPOP指令互相阻塞
生产者只负责推送,绝不启动消费循环
HTTP 请求入口(比如控制器)必须干净:只做 publish,把任务序列化后推入队列,立刻返回响应。常见错误是直接在控制器里写 while($channel->is_open()) { $channel->wait(); } ——这会让 Worker 长期阻塞,触发 Nginx 超时、连接泄漏,且无法被进程管理工具监控。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
正确做法:
- 使用
$redis->zAdd('queue:delayed', time() + 60, json_encode($job))推送带延迟的任务 - 普通即时任务用
$redis->zAdd('queue:delayed', time(), json_encode($job)) - 确保任务数据含唯一标识(如
task_id),便于幂等和日志追踪
消费者必须是独立 CLI 进程,由 Supervisor 守护
消费逻辑不能嵌在 Web 请求生命周期里,必须抽离为单独的 CLI 命令,例如 php app/command/ConsumerCommand.php 或 php webman start:consumer。这个进程要常驻运行,持续扫描 zset 中到期任务并执行。
保障稳定性的实操要点:
- 用 Supervisor 管理,配置
autostart=true和autorestart=true - 在
onWorkerStop回调中显式关闭 Redis 连接,并sleep(1)确保资源释放干净 - 处理任务前先
ZREM移出 zset,再执行;成功后不需再操作,失败则重新ZADD并增加重试次数字段 - 每轮扫描控制数量(如每次最多取 100 条),避免单次负载过高
补足可靠性细节:持久化、重试、监控缺一不可
Redis 默认内存存储,但队列任务不能只靠内存扛。虽然 zset 本身不自动持久化,但可通过以下方式加固:
- 开启 Redis 的 RDB/AOF 持久化配置(尤其 AOF
appendfsync everysec是平衡性能与安全的底线) - 任务体建议用
json_encode($data, JSON_UNESCAPED_UNICODE),带上created_at、retry_count、max_retries字段 - 消费失败时记录错误日志 + 报警(如写入 MySQL error_log 表或发企业微信通知)
- 定期检查
zcard queue:delayed和待处理积压量,设置阈值告警
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










