Webman消费者不能写在HTTP控制器里,否则会导致HTTP Worker进程阻塞、请求超时、RabbitMQ断连、重复消费及Supervisor失控;正确做法是剥离为独立CLI命令并由Supervisor守护,生产者必须设delivery_mode=>2且队列/交换机持久化,消费者须手动ACK并在finally中确保确认,同时配置心跳与连接池。

Webman消费者不能写在HTTP控制器里
这是最常踩的坑:有人直接在app/controller/OrderController.php里调用basic_consume,还加个while ($channel->is_open()) { $channel->wait(); }——这会让Webman的HTTP Worker进程彻底卡死。
- HTTP请求超时(Nginx默认60秒,PHP也常设
max_execution_time),用户看到504或白屏 - RabbitMQ报
CHANNEL_ERROR - expected 'channel.open'或主动断连 - 多个HTTP请求触发多个消费者,同一消息被重复消费(比如发两封激活邮件)
- Supervisor无法接管进程,崩溃后不重启,消息堆积无人处理
正确做法是把消费者剥离为独立CLI命令,例如app/command/OrderConsumerCommand.php,通过php webman start:consumer或php app/command/OrderConsumerCommand.php启动,并交由Supervisor守护。
生产者必须设delivery_mode => 2且队列/交换机持久化
不加delivery_mode => 2,消息只存在RabbitMQ内存里,服务重启后全丢。这不是“可能丢”,而是“一定丢”——尤其在订单、支付等关键路径上,等于没走队列。
-
delivery_mode => 1仅适合日志、埋点等可丢失场景 -
delivery_mode => 2必须搭配队列声明时的durable => true:$channel->queue_declare('order_queue', false, true, false, false) - 交换机也要持久化:
$channel->exchange_declare('order_exchange', 'direct', false, true, false) - 消息体建议用
json_encode($data)并设置content_type => 'application/json',方便下游校验结构
漏掉任意一环,RabbitMQ重启后消息就蒸发,而日志里还显示“发送成功”。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
消费者必须手动ACK,且异常分支不能跳过
auto_ack = true是开发陷阱。上线后必须关掉,否则消息一旦被取走就立即从队列删除,哪怕后续业务逻辑抛出异常或进程崩溃,消息也永远消失了。
- 必须在处理逻辑完成后显式调用
$msg->ack() - 发生异常时,应调用
$msg->nack(['requeue' => true])让消息重回队列重试 - 不能把
ack()写在try块末尾却忽略catch里的nack(),否则异常时消息直接丢失 - 建议在
finally块中确认是否已ack/nack,避免遗漏
Webman生命周期里没有自动重连机制,连接断开后不会自动恢复,所以还要配心跳(heartbeat => 60)和连接池(如用workbunny/rabbitmq组件)。
用workbunny/rabbitmq还是原生php-amqplib
选workbunny/rabbitmq省事,但只适合标准消费模型;需要精细控制重试、死信、连接异常恢复时,得上原生php-amqplib封装。
-
workbunny/rabbitmq:支持连接池、通道池、多种消费模式,配置简单,适合80%企业场景;执行composer require workbunny/rabbitmq,再跑php webman vendor:publish workbunny/rabbitmq即可 -
php-amqplib:更底层,能控制connection_timeout、read_write_timeout、retry_limit等参数,适合对可靠性要求极高的金融类业务;需自己在onWorkerStart建连接,在onWorkerStop关连接,还要监听SIGTERM信号做优雅关闭
别小看onWorkerStop里那一句$channel->close(); $connection->close();——没它,Webman热更新或平滑重启时会残留僵尸连接,RabbitMQ侧很快报“too many connections”。

















