给Yii2的HttpClient模拟超时
2026-03-04
问:
$res = $client->createRequest()
->setUrl($url)// 设置接口地址
->setMethod('POST')
->setData([
'ProjectId' => $project->Id,
'ReportId' => $reportId,
'Dir' => str_replace(Yii::$app->basePath, '',$dir),
'ReportFileName' => $report_name,
'ReportFileNameWithTag' => $report_tag_name,
'Extension' => $ext
])
->setOptions([
'timeout' => 180, // 此处三个时间超时太长不合理,改到5分钟即可
'userAgent' => 'ZPY2.0',
'sslVerifyPeer' => false,
'sslCafile' => '',
'sslCapath' => '',
CURLOPT_CONNECTTIMEOUT => 180, // connection timeout
CURLOPT_TIMEOUT => 180, // data receiving timeout
])
->setFormat(\yii\httpclient\Client::FORMAT_JSON)
->addHeaders(['Content-Type' => 'application/json'])
->send();
这个我如何模拟超时?
答:
你想测试的是:客户端请求在 Yii2 httpclient 中触发 timeout 的情况。
核心思路其实只有三种:
1 让服务端故意慢(最真实)
2 请求一个永远不返回的地址
3 人为把 timeout 调得很小
我给你几个最实用的办法。
一、最推荐:服务端 sleep(真实模拟)
如果你能控制接口服务端,最简单。
接口代码加:
sleep(200);
例如:
public function actionTest()
{
sleep(200);
return ['ok' => true];
}你的客户端:
CURLOPT_TIMEOUT => 10
10秒后就会 timeout。
二、请求一个不会返回的地址(最简单)
比如:
$url = 'http://10.255.255.1/test';
这个 IP 常用于模拟网络阻塞。
请求代码不用变。
会触发:
Operation timed out
三、故意把 timeout 调小(开发最常用)
->setOptions([ CURLOPT_CONNECTTIMEOUT => 1, CURLOPT_TIMEOUT => 1, ])
如果接口稍微慢一点就会超时。
四、模拟“连接超时”和“读取超时”
连接超时(服务器没响应)
CURLOPT_CONNECTTIMEOUT => 2
访问一个不存在的 IP。
读取超时(服务器很慢)
服务器:
sleep(30);
客户端:
CURLOPT_TIMEOUT => 5
五、抓取错误信息(非常重要)
很多人不知道 timeout 怎么判断。
if (!$res->isOk) {
$error = $res->getContent();
var_dump($res->statusCode);
}或者:
try {
$res = $request->send();
} catch (\Exception $e) {
echo $e->getMessage();
}六、推荐的完整测试代码
try {
$res = $client->createRequest()
->setUrl($url)
->setMethod('POST')
->setData($data)
->setOptions([
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 3,
])
->send();
} catch (\Throwable $e) {
echo "timeout测试成功\n";
echo $e->getMessage();
}七、企业级最真实的办法(推荐)
使用 nginx 限速 或 tc 网络延迟
例如:
tc qdisc add dev eth0 root netem delay 3000ms
模拟 3 秒网络延迟。
八、很多人不知道的 Yii2 坑
你代码里有一个问题:
'timeout' => 180
这个 不是 curl timeout。
真正起作用的是:
CURLOPT_TIMEOUT CURLOPT_CONNECTTIMEOUT
九、最简单一句话模拟
把 timeout 改成:
CURLOPT_TIMEOUT => 1
然后请求一个正常接口。
基本就能稳定复现。
在 Yii2 yii\httpclient\Client 里,你代码中的:
->setOptions([ 'timeout' => 180, ])
很多人会误解,它其实不是 Yii 自己定义的参数,而是 传给底层 cURL 的一个 option。但它的作用和 CURLOPT_TIMEOUT 是同一个东西。
不过需要搞清楚 三个不同的超时概念。
一、timeout 的真实作用
'timeout' => 180
等价于:
CURLOPT_TIMEOUT => 180
意思是:
整个 HTTP 请求最长允许执行 180 秒
包括:
DNS 解析
TCP 建立连接
SSL 握手
发送请求
等待服务器响应
接收响应数据
超过 180 秒就会被 cURL 强制终止。
二、三个常见超时参数区别
| 参数 | 作用 | 触发阶段 |
|---|---|---|
CURLOPT_CONNECTTIMEOUT | 建立连接超时 | TCP连接阶段 |
CURLOPT_TIMEOUT / timeout | 整个请求最大时间 | 全流程 |
CURLOPT_TIMEOUT_MS | 毫秒级超时 | 全流程 |
三、一个最清晰的理解方式
假设:
CURLOPT_CONNECTTIMEOUT = 5 CURLOPT_TIMEOUT = 20
流程就是:
0s -----------5s---------------------20s 连接阶段 请求+响应阶段
情况:
1️⃣ 如果 5秒连不上服务器
报错:
Connection timed out
2️⃣ 如果 连接成功但服务器一直不返回
到 20秒 报:
Operation timed out
四、你代码里其实写重复了
你现在是:
'timeout' => 180, CURLOPT_CONNECTTIMEOUT => 180, CURLOPT_TIMEOUT => 180,
这里:
timeout = CURLOPT_TIMEOUT
所以 写了两次一样的东西。
五、推荐生产写法
建议这样写最清晰:
->setOptions([ CURLOPT_CONNECTTIMEOUT => 5, // 建立连接最多5秒 CURLOPT_TIMEOUT => 30, // 整个请求最多30秒 ])
如果是 内部服务调用:
CONNECTTIMEOUT = 2 TIMEOUT = 10
就够了。
六、你代码还有两个小问题
'sslVerifyPeer' => false, 'sslCafile' => '', 'sslCapath' => '',
这些是 Guzzle 的参数,
在 Yii2 httpclient 里 不会生效。
正确写法是:
CURLOPT_SSL_VERIFYPEER => false CURLOPT_SSL_VERIFYHOST => false
七、给你一份 Yii2 最标准 HTTP 请求模板
$res = $client->createRequest()
->setUrl($url)
->setMethod('POST')
->setData($data)
->addHeaders([
'Content-Type' => 'application/json',
'User-Agent' => 'ZPY2.0'
])
->setOptions([
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
])
->setFormat(\yii\httpclient\Client::FORMAT_JSON)
->send();八、很多高级程序员才知道的一个坑
如果你写:
CURLOPT_TIMEOUT => 30
但服务器 一直在传数据(哪怕很慢),
请求 不会超时。
因为 cURL 认为“数据还在流动”。
真正限制这个的是:
CURLOPT_LOW_SPEED_TIME CURLOPT_LOW_SPEED_LIMIT
发表评论: