consumer进程根本没起来,先看process注解和配置是否生效:类需带#[process]或#[consumer]注解、继承对应基类,且processes.php中必须注册consumerprocess类;启动日志应出现process[xxx] start,否则注解未识别或类未加载。

Consumer进程根本没起来,先看 Process 注解和配置是否生效
很多“不消费”其实是进程压根没启动,而不是卡在中间某环。Hyperf 的 Consumer 进程靠 #[Process] 注解驱动,不是靠 async_queue.php 里的 processes 字段——这个字段在新版里已废弃,设成非零值反而会干扰启动逻辑。
检查点如下:
-
app/Process/下的消费者类是否继承Hyperf\AsyncQueue\Process\ConsumerProcess(Redis 队列)或Hyperf\Amqp\Message\ConsumerMessage(AMQP) - 类上是否明确写了
#[Process](Redis)或#[Consumer](AMQP),注意是方括号注解,不是@Process -
config/autoload/processes.php是否包含Hyperf\AsyncQueue\Process\ConsumerProcess::class—— 缺了这行,框架连扫描都不扫描你的 Consumer 类 - 启动后日志里有没有类似
Process[AsyncQueueProcess] start或Process[DemoConsumer] start的记录;没有就说明注解未被识别或类路径未被自动加载
Redis连接池空转或超时,blpop 卡住不返回
Hyperf Redis 队列底层用的是 BLPOP 阻塞式拉取,一旦连接池异常、超时或 Redis 服务不可达,blpop 就会一直挂起,看起来像“不消费”,实则是卡在等待连接响应上。
重点排查:
-
config/autoload/redis.php中对应 pool 的connect_timeout和wait_timeout是否设得过大(比如 30 秒),导致故障时长时间无响应;建议调为3.0和2.0 - 连接池
min_connections是否太小(如设为 1),高并发下连接耗尽,后续blpop等不到可用连接 - 执行
redis-cli -p 6379 llen "queue:default"确认队列里确实有数据;再手动跑blpop queue:default 1,看是否能立即返回——如果也卡住,基本锁定是 Redis 服务或网络问题 - 检查
runtime/logs/hyperf.log里有没有Connection refused、read error on connection或Redis server went away类错误
进程起来了但秒退,盯紧 FatalError 和 abnormal exit
Consumer 进程异常退出时,Swoole 日志只显示 worker(pid=12345, id=2) abnormal exit,不打堆栈——这是最典型的“静默挂掉”。它大概率由 FatalError 触发,比如 Class not found、Call to undefined method、Allowed memory size exhausted。
必须做的动作:
- 在 Consumer 的
handle()方法最外层加try/catch(\Throwable $e),并强制记录完整堆栈到日志,例如:try { // 原有逻辑 } catch (\Throwable $e) { \Hyperf\Utils\Coroutine::usleep(1000); \Psr\Log\LoggerInterface::error('Consumer handle fatal', ['exception' => $e]); throw $e; } - 确认没在
handle()里用exit或die—— 它们绕过所有异常捕获,直接杀进程 - 检查消息体反序列化:如果用了
json_decode($data),但$data是空、非法 UTF-8 或超深嵌套,json_last_error()会返回错误,后续调用对象方法就崩;建议开头加if (!is_string($data) || !json_validate($data)) { $this->release(); return; }
消费了但没效果,查 concurrent.limit 和失败队列堆积
进程在跑、日志没报错、消息也进了 Redis,但业务逻辑就是不执行——这时候大概率是任务被吞进失败队列,或者并发限制太严导致“看似没动”。
验证方式:
- 查 Redis 当前队列长度:
redis-cli -p 6379 llen "queue:default"(默认 channel)和llen "queue:default:failed",后者非空说明任务反复失败后被移入失败队列 - 检查
config/autoload/async_queue.php中的concurrent.limit是否设得太小(如 1),单个进程一次只能处理一个任务,若该任务阻塞或超时,后续任务就得排队等 - 确认
handle_timeout是否过短(如设为 1 秒),而实际业务要 2 秒才做完,结果每次都被判定超时重试,形成假性“不消费” - 别忽略
retry_seconds配置,如果设成[1, 3, 6, 12],失败后第二次重试要等 3 秒,中间这段时间看起来就是“停着不动”
真正难缠的不是报错,而是那些没报错、进程活着、日志安静、但任务永远卡在队列里不动的情况——它往往藏在连接池参数、concurrent.limit 与 handle_timeout 的微妙失配里,需要对照 Redis 实时长度和进程日志交叉验证。











