Guzzle read_timeout测试异常:设置超时后未触发超时问题排查
问题分析与解决方案
核心问题:服务端输出未即时发送
你的服务端脚本虽然做了循环延迟输出,但PHP默认开启输出缓冲,fwrite的内容会被暂存在缓冲区中,不会立刻发送给客户端。而Guzzle的read_timeout是检测两次接收数据之间的间隔时长,不是连接建立后的总时长——客户端在2秒内根本没收到任何数据片段,自然不会触发超时。
修复步骤
1. 修正服务端脚本(强制即时输出)
修改服务端代码,每次写入后强制刷新缓冲区,确保数据立刻发送到客户端:
<?php // 关闭全局输出缓冲,或手动终止缓冲链 ini_set('output_buffering', 'off'); ob_end_flush(); $stream = fopen('php://output', 'w'); $start_time = time(); for ($i = 0; $i < 3; $i++) { fwrite($stream, "Data chunk $i\n"); // 强制刷新输出缓冲区,让数据即时传送给客户端 fflush($stream); sleep(6); } $data = "\nStart time: $start_time\nEnd time: " . time(); fwrite($stream, $data); fflush($stream); fclose($stream); ?>
2. 客户端的额外验证配置
- 显式指定Guzzle使用CurlHandler,避免自动切换到不支持
read_timeout的其他Handler:require 'vendor/autoload.php'; use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\CurlHandler; // 强制使用CurlHandler $stack = HandlerStack::create(new CurlHandler()); $client = new Client([ 'handler' => $stack, 'read_timeout' => 2, 'stream' => true ]); try { $response = $client->request('GET', 'http://localhost:8002/server_test.php', [ 'stream' => true, 'read_timeout' => 2 ]); $body = $response->getBody(); while (!$body->eof()) { echo $body->read(1024); } } catch (RequestException $e) { echo "Error: " . $e->getMessage(); } - 注意:
read_timeout仅在客户端已收到部分数据、后续超过指定时长未收到下一批数据时触发。如果服务端全程无数据输出,触发的会是connect_timeout或timeout(总连接超时)。
3. 额外检查项
- 查看PHP的
output_buffering配置:通过phpinfo()确认全局缓冲未开启过大的阈值,避免数据被滞留。 - 用命令行运行客户端脚本:部分GUI客户端会等待响应完成才显示内容,干扰超时测试结果,命令行环境能更直观观察实时输出和错误。
测试结果验证
修改完成后,客户端收到第一个数据块后,会等待6秒(超过设置的2秒read_timeout),此时会触发RequestException,抛出类似cURL error 28: Operation timed out after 2000 milliseconds with 12 bytes received的超时错误。
内容的提问来源于stack exchange,提问作者TomFT
相关产品推荐
相关产品推荐

