file_get_contents与default_socket_timeout的关系

今天公司遇到一个问题,发现file_get_contents拉取一个远程HTTP接口,花费了接近2分钟。

排查发现是对端服务不响应,导致file_get_contents大量的hang住,影响了部分流量。

问题排查

线上max_execution_time=120秒,也的确发现很多请求都打印到了slowlog中。

我们知道,php-fpm处理请求超时的方法,就是kill杀死php-fpm worker进程,让其强制退出,所以file_get_contents不会无限阻塞。

同时也发现奇怪的问题,因为file_get_contents的超时时间受default_socket_timeout = 60影响,其实应该60秒就超时了,但为什么仍旧出现了很多被杀死的fpm呢?

因为PHP的串行执行模型,可以凭直觉猜到file_get_contents底层的超时控制恐怕有猫腻,所以最快的方法就是strace看一下系统调用:

你会发现,非阻塞Connect后,利用poll等待网络事件传入了60秒超时时间,后续发送请求没有利用Poll而是直接循环写出。

最可怕的是,后续等待应答到来,每次挂起在Poll上等待可读事件都是60秒超时,直到应答读取完整,这就会导致总执行时间可能远远超出60秒。

比如:对端每50秒发送1个字节回来,一共有100个字节,那么总耗时就是50 * 100 = 5000秒,要1个多小时呢。

解决方法

建议还是用curl库,通过connect_timeout与timeout配置,严格控制连接与请求的完整耗时。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~