php并发请求(multi_curl)

  • baagee 发布于 2018-12-08 13:55:54
  • 分类:PHP
  • 100 人围观
  • 0 人喜欢

好久没写文章啦,比较忙,也没学习新的东西,前不久域名出了问题,从新备案后前几天恢复了。

今天讲一下php中如何提高并发请求一批接口,get请求示例。

常规的curl请求一批接口:

// 循环多次请求接口
$start_time = microtime(true);
$res        = [];
$url        = 'http://localhost:8080/test/cache';
for ($i = 0; $i < 10; $i++) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $res[$i] = curl_exec($ch);
}
//var_dump($res);
echo 'time=' . (microtime(true) - $start_time) . PHP_EOL;

通过foreach循环请求每个接口,这样的话如果每个接口请求需要1秒,那么10个就需要10秒,耗时长,效率很低。

如何提高这个请求一批接口的性能呢?接下来就用到了multi_curl了。

简单介绍一下要用到的方法:

/** 函数作用:返回一个新cURL批处理句柄
@return resource 成功返回cURL批处理句柄,失败返回false
 */
resource curl_multi_init ( void )

/** 函数作用:向curl批处理会话中添加单独的curl句柄
@param $mh 由curl_multi_init返回的批处理句柄
@param $ch 由curl_init返回的cURL句柄
@return resource 成功返回cURL批处理句柄,失败返回false
 */
int curl_multi_add_handle ( resource $mh , resource $ch )

/** 函数作用:运行当前 cURL 句柄的子连接
@param $mh 由curl_multi_init返回的批处理句柄
@param $still_running 一个用来判断操作是否仍在执行的标识的引用
@return 一个定义于 cURL 预定义常量中的 cURL 代码
 */
int curl_multi_exec ( resource $mh , int &$still_running )

/** 函数作用:等待所有cURL批处理中的活动连接
@param $mh 由curl_multi_init返回的批处理句柄
@param $timeout 以秒为单位,等待响应的时间
@return 成功时返回描述符集合中描述符的数量。失败时,select失败时返回-1,否则返回超时(从底层的select系统调用).
 */
int curl_multi_select ( resource $mh [, float $timeout = 1.0 ] )

/** 函数作用:移除cURL批处理句柄资源中的某个句柄资源
说明:从给定的批处理句柄mh中移除ch句柄。当ch句柄被移除以后,仍然可以合法地用curl_exec()执行这个句柄。如果要移除的句柄正在被使用,则这个句柄涉及的所有传输任务会被中止。
@param $mh 由curl_multi_init返回的批处理句柄
@param $ch 由curl_init返回的cURL句柄
@return 成功时返回0,失败时返回CURLM_XXX中的一个
 */
int curl_multi_remove_handle ( resource $mh , resource $ch )

/** 函数作用:关闭一组cURL句柄
@param $mh 由curl_multi_init返回的批处理句柄
@return void
 */
void curl_multi_close ( resource $mh )

/** 函数作用:如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流
@param $ch 由curl_init返回的cURL句柄
@return string 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流。
 */
string curl_multi_getcontent ( resource $ch )

简单使用:

$start_time = microtime(true);
$ch_arr     = [];//保存单个的curl_init资源
$mh = curl_multi_init();
for ($i = 0; $i < 10; $i++) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $ch_arr[$i] = $ch;
    curl_multi_add_handle($mh, $ch);//添加
}
$runing = 1;
do {
    curl_multi_exec($mh, $runing);
    //系统会不停地执行curl_multi_exec()函数。这样可能会轻易导致CPU占用很高
} while ($runing > 0);
$res = [];
foreach ($ch_arr as $ch) {
    $res[] = curl_multi_getcontent($ch);//获取请求结果
    curl_multi_remove_handle($mh, $ch);//移除
}

curl_multi_close($mh);
//var_dump($res);
echo 'time=' . (microtime(true) - $start_time) . PHP_EOL;

解决一下cpu占用高的问题:

// 并发优化CPU占用高
$start_time = microtime(true);
$ch_arr     = [];
$mh         = curl_multi_init();
for ($i = 0; $i < 10; $i++) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $ch_arr[$i] = $ch;
    curl_multi_add_handle($mh, $ch);
}
$runing = 1;

do {
    $mc = curl_multi_exec($mh, $runing);
//当返回值等于CURLM_CALL_MULTI_PERFORM时,表明数据还在写入或读取中,执行循环,
//当第一次$ch句柄的数据写入或读取成功后,返回值变为CURLM_OK,跳出本次循环,进入下面的大循环之中
} while ($mc == CURLM_CALL_MULTI_PERFORM);

while ($runing && $mc == CURLM_OK) {
    //阻塞直到cURL批处理连接中有活动连接。成功时返回描述符集合中描述符的数量。失败时,select失败时返回-1
    if (curl_multi_select($mh) != -1) {
        //$mh批处理中还有可执行的$ch句柄,curl_multi_select($mh) != -1程序退出阻塞状态。
        do {
            //有活动连接时执行,避免了每次都执行exec
            $mc = curl_multi_exec($mh, $runing);
        } while ($mc == CURLM_CALL_MULTI_PERFORM);
    }
}

$res = [];
foreach ($ch_arr as $ch) {
    $res[] = curl_multi_getcontent($ch);
    curl_multi_remove_handle($mh, $ch);
}

curl_multi_close($mh);
//var_dump($res);
echo 'time=' . (microtime(true) - $start_time) . PHP_EOL;

还存在优化的空间, 当某个URL请求完毕之后尽可能快的去处理它, 边处理边等待其他的URL返回, 而不是等待那个最慢的接口返回之后才开始处理等工作, 从而避免CPU的空闲和浪费。

$res        = [];
$start_time = microtime(true);
$mh = curl_multi_init();
for ($i = 0; $i < 10; $i++) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($mh, $ch);
}

$runing = 1;

do {
    $mc = curl_multi_exec($mh, $runing);
//当返回值等于CURLM_CALL_MULTI_PERFORM时,表明数据还在写入或读取中,执行循环,
//当第一次$ch句柄的数据写入或读取成功后,返回值变为CURLM_OK,跳出本次循环,进入下面的大循环之中
} while ($mc == CURLM_CALL_MULTI_PERFORM);

while ($runing && $mc == CURLM_OK) {
    //阻塞直到cURL批处理连接中有活动连接。成功时返回描述符集合中描述符的数量。失败时,select失败时返回-1
    if (curl_multi_select($mh) != -1) {
        //$mh批处理中还有可执行的$ch句柄,curl_multi_select($mh) != -1程序退出阻塞状态。
        do {
            //有活动连接时执行
            $mc = curl_multi_exec($mh, $runing);
//            echo 'curl_multi_exec' . PHP_EOL;
        } while ($mc == CURLM_CALL_MULTI_PERFORM);
    }
    while ($done = curl_multi_info_read($mh)) {
//        $info                                     = curl_getinfo($done['handle']);
//        $error                                    = curl_error($done['handle']);
        $results = curl_multi_getcontent($done['handle']);
//        echo 'curl_multi_getcontent' . PHP_EOL;
        $res[] = $results;
        curl_multi_remove_handle($mh, $done['handle']);
        curl_close($done['handle']);
    }
}
curl_multi_close($mh);
//var_dump($res);
echo 'time=' . (microtime(true) - $start_time) . PHP_EOL;

这样性能就更好了。

最后执行时间依次是:



转载请说明出处:baagee博客 » php并发请求(multi_curl)
标签: php curl

评论

点击图片切换
还没有评论,快来抢沙发吧!