PHP使用curl multi要注意的问题:每次使用curl multi同时并发多少请求合适
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/loophome/article/details/53266814
PHP的curl multi可以使用多线程处理http请求,一定程度上可以提高请求接口的效率。但是,启用多线程也是会消耗资源的事情,那么每次curl multi同时并发多少个请求合适呢?
接下来做了以下一个实验,在开始的时候,先说说实验的结论:
实验结论
1)首先要检查发起请求服务器的网络带宽是否正常,避免请求服务器出现带宽瓶颈。
2)curl multi并发请求并发数有一个阈值,过高的并发不能提升效率,反而会导致请求不成功,这个阈值与服务端的性能有关。
3)CURLOPT_TIMEOUT必须跟进实际业务设置合适的值
实验代码:通过curl multi请求远程服务器上的一个接口,接口只是简单的返回字符串’1’,验证请求成功的比例。
<?php
$max_request = $argv[1];
$ch_list = array();
$multi_ch = curl_multi_init();
for ($i = 1;$i <= $max_request;++$i) {
$ch_list[$i] = curl_init(“http://www.xxx.com/a.php”);
curl_setopt($ch_list[$i], CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch_list[$i], CURLOPT_TIMEOUT, 10);
curl_multi_add_handle($multi_ch, $ch_list[$i]);
}
$active = null;
do {
$mrc = curl_multi_exec($multi_ch, $active); //处理在栈中的每一个句柄。无论该句柄需要读取或写入数据都可调用此方法。
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
//Note:
//该函数仅返回关于整个批处理栈相关的错误。即使返回 CURLM_OK 时单个传输仍可能有问题。
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($multi_ch) != -1) {//阻塞直到cURL批处理连接中有活动连接。
do {
$mrc = curl_multi_exec($multi_ch, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
//获取http返回的结果
$true_request = 0;
foreach ($ch_list as $k => $ch) {
$result = curl_multi_getcontent($ch);
curl_multi_remove_handle($multi_ch,$ch);
curl_close($ch);
if ($result == 1) {
$true_request += 1;
}
}
curl_multi_close($multi_ch);
echo $true_request, PHP_EOL;
实验结果:当并发请求次数大于600之后,成功请求次数并非线性关系,而是在650左右浮动。到目标服务器检查nginx日志,发现请求成功的日志一共有45925条,而PHP程序返回成功请求的一共有45056条。
此时,猜想,curl请求没有发出,或者返回值并没有被成功接收。
我们需要通过curl error错误码,看看发生了什么事情,这次我们使用并发为800作为一个例子,修改获取curl返回值的循环,打印出curl的错误码:
foreach ($ch_list as $k => $ch) {
$result = curl_multi_getcontent($ch);
$errstr = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_multi_remove_handle($multi_ch,$ch);
curl_close($ch);
if ($result == 1) {
$true_request += 1;
} else {
echo “{$code}:{$errstr}”, PHP_EOL;
}
}
,
当请求失败时打印HTTP状态码,以及curl的errstr 值,却发现HTTP状态码为0,然而curl的errstr并没有值。没有errstr的值,似乎无从入手了,后面使用strace进行尝试,发现请求失败的recvfrom返回。
recvfrom(370, 0x3cccc68, 16384, 0, 0, 0) = -1 ECONNRESET (Connection reset by peer)
recvfrom(390, “”, 16384, 0, NULL, NULL) = 0
ECONNRESET (Connection reset by peer)说明的是服务端关闭了该请求(写是关闭), recvfrom返回0也是服务端关闭了请求(读时关闭),看来最大的问题出在server端上!
后记:如何解决Connect time out的问题。
通过数据分析curl耗时,并设置CURLOPT_TIMEOUT的设置在合适的时间内。之前做了一个监控的daemon程序,CURLOPT_TIMEOUT设置为3s,发现有非常多的请求出现Connect time out的情况。CURLOPT_TIMEOUT设置为15s后,Connect time out的情况就少了,那么CURLOPT_TIMEOUT应该设置为多少比较合适呢?这个可以先收集请求的响应时间,接下来对CURLOPT_TIMEOUT进行优化。
curl_getinfo()函数可以返回几个有助于我们分析请求时间的指标:
参数名称 | 说明 |
total_time | curl所花费的总时间 |
namelookup_time | 域名解析所花费的时间 |
connect_time | 连接目标服务器所花费的时间 |
redirect_time | 重定向所花费的时间 |
starttransfer_time | 数据传输开始时间 |
PS:指标的单位均是秒(seconds),如果要计算数据传输的时间,可以通过total_time – starttransfer_time的差值获取。
每次请求结束后记录total_time,通过数据分析,得出合适的CURLOPT_TIMEOUT设置值。
从上图可以看出,由于服务器端请求响应时间非常不稳定,但是趋势是响应时间越大的请求数会越来越小。因此,CURLOPT_TIMEOUT我这面设置为15s是比较合适的,而监控项目上线后,也很小会出现Connect time out 的情况。
转载请注明:SuperIT » PHP使用curl multi要注意的问题:每次使用curl multi同时并发多少请求合适