Hyperf中Redis List队列的BLPOP超时应设为1~3秒,需匹配客户端socket超时≥BLPOP timeout≥Redis server timeout,避免伪空等待、连接断连或消息丢失。

Hyperf里用Redis List做队列,blpop 是核心但得配好超时
Hyperf 的 hyperf/async-queue 默认就是基于 Redis List 实现的,底层调用的是 blpop。它不像 Kafka 那样有确认机制,所以「丢消息」风险真实存在——比如消费者进程崩溃、没来得及 ack 就退出了。
关键参数在 config/autoload/async_queue.php 里:timeout 控制 blpop 等待时长,默认是 2 秒;太短会频繁轮询浪费 CPU,太长则延迟高。生产环境建议设为 1~3 秒之间。
-
channel对应 Redis 中的 list key 名,比如queue:default -
retry_seconds不是重试间隔,而是失败后把 job 推进 retry list 的延迟时间,不是立即重试 - 如果想手动用
lpush/blpop写裸逻辑,注意blpop必须带 timeout,否则阻塞住整个协程
Stream 更可靠,但 hyperf/async-queue 不原生支持
Redis Stream 天然支持消费者组(consumer group)、ACK、pending 列表,比 List 更适合做消息队列。但 hyperf/async-queue 当前(v3.1)只实现了 RedisDriver 基于 List 的驱动,不支持 Stream。
如果你真要用 Stream,得自己写驱动:实现 DriverInterface,重点重写 push()(用 xadd)、pop()(用 xreadgroup)、ack()(用 xack)。别直接改源码,通过 DriverFactory 注入新类即可。
- Stream 的
group名必须提前用xgroup create创建,否则xreadgroup报错NOGROUP No such key 'xxx' or consumer group -
xreadgroup的count参数控制单次拉多少条,Hyperf 默认并发是10,但 Stream 拉太多容易堆积 pending,建议设为1~3 - 别漏掉
xclaim处理 pending 消息——消费者宕机后,其他实例要主动认领超时未 ack 的消息
为什么 processes 设成 0 才能跑起来
Hyperf 的异步队列消费进程依赖 Process 注解启动,不是靠配置里的 processes 字段起 worker。这个字段实际已被废弃,设成非零值反而会让框架误判为旧版启动方式,导致消费进程不拉消息。
正确姿势是:在 app/Process/ 下建一个继承 ConsumerProcess 的类,加上 #[Process] 注解,并指定 $pool 属性指向配置里的队列名。
- 多个消费者共用一个
channel时,Redis List 天然负载均衡(blpop是原子的),但 Stream 必须用不同consumer名才能分摊消息 - 运行
php bin/hyperf.php start后,看日志里有没有Process[xxx_consumer.0] start,没有就说明注解没生效或类没被扫描到 -
nums参数决定启几个进程,不是协程数——每个进程内才是协程并发消费
消息丢了怎么办?先看是不是没处理完就返回了
Hyperf 的 Job 类里,handle() 方法执行完才算成功。如果里面抛了未捕获异常、或用了 exit/die、或协程被意外 cancel,框架会自动把 job 推进 failed queue(key 是 failed:xxx),不会直接丢。
但以下情况真会丢:
- Redis 连接池耗尽,
push()失败但代码没判返回值,静默跳过 - 消费者进程被
kill -9干掉,且handle_timeout设置过长(比如 60 秒),导致 job 卡在 pending 状态直到超时自动释放 - 用
redis-cli手动del了队列 key,List 数据清空,Stream 数据还在但 group offset 丢失
真正要保消息不丢,得上 RabbitMQ 或 Kafka。Redis 做队列,本质是“尽力而为”,别把它当 MQ 用。


















