近期在使用rabbitmq时检测到生产环境频繁报错::
{"message":"Broken pipe or closed connection","context":{"exception":{"class":"PhpAmqpLib\\Exception\\AMQPConnectionClosedException","message":"Broken pipe or closed connection"
}}
排查后基本确定是消费端进程在开启消费阻塞等待后,SLB出于资源的有效利用考虑会对空闲的TCP连接进行回收,这样进程所持有的socket文件资源描述符就会找不到,报错发生在这段代码(php-amqp-lib库):
if (!is_resource($this->sock) || feof($this->sock)) {
$this->close();
throw new AMQPConnectionClosedException('Broken pipe or closed connection');
}
事实上,队列消费端常驻进程会在很多情况下断掉tcp连接,例如出现网络抖动等非人为因素,为了保持tcp的持久连接,rabbitmq提供了两种方式:
- 心跳检测
心跳检测是rabbitmq保持持久连接的一种实现方式,开启心跳后客户端会在每心跳超时时间/2内发送一个心跳包到服务端,证明是处于有效连接的,如果超过这个时间没有收到心跳,客户端就会自动发起重连。
new AMQPStreamConnection($host, $port, $user, $password, $vhost, false, 'AMQPLAIN', null, 'en_US', 10.0, $rwTimeout, null, false, $heartBeat);
通过以上方式可以设置心跳时间,默认为60s;开启心跳后问题解决,没有出现进程断掉的报错。
- tcp keepalive
tcp keepalive在内部也实现了类似心跳的机制,建议两者选择其一就行,实现方式:
public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$insist = false,
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
$connection_timeout = 3.0,
$read_write_timeout = 3.0,
$context = null,
$keepalive = false,
$heartbeat = 0,
$channel_rpc_timeout = 0.0,
$ssl_protocol = null
)
在实例化AMQPStreamConnection对象时,$keepalive参数设置为true即可。