PHP使用curl multi要注意的问题:每次使用curl multi同时并发多少请求合适

PHP的curl multi可以使用多线程处理http请求,一定程度上可以提高请求接口的效率。但是,启用多线程也是会消耗资源的事情,那么每次curl multi同时并发多少个请求合适呢?
接下来做了以下一个实验,在开始的时候,先说说实验的结论:
实验结论
1)首先要检查发起请求服务器的网络带宽是否正常,避免请求服务器出现带宽瓶颈。
2)curl multi并发请求并发数有一个阈值,过高的并发不能提升效率,反而会导致请求不成功,这个阈值与服务端的性能有关。
3)CURLOPT_TIMEOUT必须跟进实际业务设置合适的值

实验代码:通过curl multi请求远程服务器上的一个接口,接口只是简单的返回字符串'1',验证请求成功的比例。

$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请求没有发出,或者返回值并没有被成功接收。

PHP使用curl multi要注意的问题:每次使用curl multi同时并发多少请求合适_第1张图片

我们需要通过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设置值。
PHP使用curl multi要注意的问题:每次使用curl multi同时并发多少请求合适_第2张图片
从上图可以看出,由于服务器端请求响应时间非常不稳定,但是趋势是响应时间越大的请求数会越来越小。因此,CURLOPT_TIMEOUT我这面设置为15s是比较合适的,而监控项目上线后,也很小会出现 Connect time out 的情况。


你可能感兴趣的:(PHP)