Laravel 5.7+Guzzle批量请求出现cURL error 18:传输中断仍有未读数据
解决Guzzle Pool批量请求中cURL Error 18的问题
我之前处理过不少类似的Guzzle批量请求超时问题,结合你用Laravel 5.7 + 最新Guzzle的场景,每次恰好2分钟触发cURL error 18: transfer closed with outstanding read data remaining,大概率是下面几个原因导致的,给你整理了对应的解决思路:
1. 服务器端超时阈值触发
这个2分钟的固定时间点很关键——十有八九是目标服务器或者你的应用服务器设置了2分钟的超时限制。当批量请求的总耗时超过这个阈值,服务器会主动切断连接,导致Guzzle还没读完数据就被中断,从而抛出这个错误。
- 解决办法:
- 先查目标网站的API文档,看看有没有单请求或批量请求的超时限制,如果有的话,把请求拆成更小的批次,减少每批处理的URL数量;
- 在Guzzle客户端里显式设置单请求的超时和连接超时,避免单个请求拖慢整个池:
$client = new \GuzzleHttp\Client([ 'timeout' => 60, // 单个请求最多等60秒 'connect_timeout' => 10, // 连接阶段最多等10秒 ]); - 检查你的Laravel应用服务器配置,比如Nginx的
proxy_read_timeout、Apache的Timeout,如果设置了120秒(2分钟),可以适当调高。
2. Content-Length隐式不匹配
你说没手动设置Content-length,但Guzzle默认会自动计算并添加这个头部。如果目标服务器对这个头部的校验很严格,而你的批量请求里有部分请求体是动态生成的(比如POST请求的参数变化),就可能出现实际传输的数据和头部声明的长度不匹配,服务器发现后直接关闭连接。
- 解决办法:
- 禁用自动的
Content-length,改用分块传输(Transfer-Encoding: chunked),这样就不需要提前声明长度:$requests = function ($urls, $headers) { foreach ($urls as $url) { yield new \GuzzleHttp\Psr7\Request( 'GET', // 根据你的请求方法调整 $url, array_merge($headers, ['Transfer-Encoding' => 'chunked']) ); } }; - 如果是POST/PUT请求,确保每个请求的请求体内容是稳定的,避免动态修改导致长度变化。
- 禁用自动的
3. Keep-Alive连接复用的坑
批量请求时Guzzle会默认复用Keep-Alive连接,但如果目标服务器对连接复用有严格限制(比如最大复用次数、连接空闲超时),当复用次数过多或者连接空闲超过阈值,服务器就会强制关闭连接,刚好触发这个错误。
- 解决办法:
- 直接禁用Keep-Alive,强制每个请求用新连接:
$client = new \GuzzleHttp\Client([ 'headers' => ['Connection' => 'close'], ]); - 或者调整Pool的并发数,不要让并发太高,给服务器留缓冲空间:
$pool = new \GuzzleHttp\Pool($client, $requests($urls, $headers), [ 'concurrency' => 5, // 建议先从5-10开始试,根据目标服务器情况调整 'fulfilled' => function ($response, $index) { // 处理成功响应逻辑 }, 'rejected' => function ($reason, $index) { // 处理错误逻辑 }, ]);
- 直接禁用Keep-Alive,强制每个请求用新连接:
4. 目标服务器的请求限制
数据量增大后,你可能触发了目标服务器的请求频率或流量阈值,服务器为了保护自身,会主动中断部分连接来限流。
- 解决办法:
- 在批量请求里加个小延迟,控制请求频率:
$requests = function ($urls, $headers) { foreach ($urls as $url) { usleep(500000); // 每次请求间隔0.5秒,根据情况调整 yield new \GuzzleHttp\Psr7\Request('GET', $url, $headers); } }; - 如果可以的话,联系目标网站的管理员,确认是否有请求限制,看看能不能申请更高的配额。
- 在批量请求里加个小延迟,控制请求频率:
额外排查小技巧
- 打开Guzzle的调试模式,看看详细的请求日志,能帮你定位到底是哪个请求触发了错误:
$client = new \GuzzleHttp\Client([ 'debug' => true, ]); - 单独测试一个数据量较大的请求,看看会不会报错,排除批量并发本身的问题。
内容的提问来源于stack exchange,提问作者Radical_Activity
相关产品推荐
相关产品推荐

