AMQP error while attempting pushRaw: fwrite(): send of 3517 bytes failed with errno=11

场景:laravel + laravel-queue-rabbitmq + php-amqplib 实现laravel rabbimq中间件相关功能。

现象:之前很长一段时间正常运行相关业务,最近在同一台服务器上部署了另外一个laravel项目,该项目也需要用到rabbimq,两天后出现异常,查看日志报错如下:

local.ERROR: AMQP error while attempting pushRaw: fwrite(): send of 3517 bytes failed with errno=11 资源暂时不可用

解决1:看到报错,第一时间想法是rabbimq这边的问题, 考虑会不会是这边单个messge的大小超了。 后面通过在 测试环境、开发环境 测试都正常,所以确定不是rabbitmq这边的问题。

解决2:找到业务逻辑代码,修改进入队列的单个数据大小,发现可以正常进入队列 不报错了。后面观察了一段时间发现这个报错还是会偶发,问题没有得到根治 也没有真正找出原因。

解决3:究其源码 找到报错的一行 在php-amqplib 中 streamIO.php 中的write方法 fwrite($this->sock,$data,8192) ,源码解析为 写入队列的时候会调用这边的write方法,进行socket缓冲区写入,也就是在写入socket的时候超出了缓冲区的大小导致报错。

#查看服务器tcp socket 读写缓冲区大小
cat /proc/sys/net/ipv4/tcp_wmem
4096    16384   4194304    #第一个值是为socket发送缓冲区分配的最少字节数;第二个值是默认值(该值会被wmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是发送缓冲区空间的最大字节数(该值会被wmem_max覆盖)。
sysctl -q net.ipv4.tcp_wmem
net.ipv4.tcp_wmem = 4096        16384   4194304 

通过信息可以看到 当前缓冲区默认情况下支持最大16384 bytes ,通过之前的打印 看到单个message 通过php-amqplib 写入socket的时候 确实超过了这个限制,导致异常报错。

最终解决:修改服务器tcp socket 写入缓冲区大小,改大一点。

#修改写入缓冲区大小
echo "net.ipv4.tcp_wmem=4096  256960 4194304 " >> /etc/sysctl.conf
#使其生效
sysctl -p

最终通过测试发现queue正常了 也不报错了。

思考:虽然通过业务逻辑修改单个message大小 或者 修改服务器 TCP SOCKET写入缓冲区大小 都能解决这个报错问题、也找到具体报错的原因了,但是 我们在设计queue 单个message时 也要注意 不要让单个message承载的东西太多