php實現併發處理之curl篇

原文地址:https://yq.aliyun.com/articles/27416?spm=5176.8091938.0.0.yloNYW

php在併發處理方面的確不如java好。但是也有一些方法可以實現併發處理。比如使用curl就可以實現url的併發請求。

看到網上有人說使用curl會導致阻塞,即所有的請求數據都獲取完畢後一併返回,然後再進行數據處理。而不是獲取一個請求的數據就處理一個數據。其實這種說法是不對的,只能說明他在代碼實現上有問題。

在php官方找了段導致阻塞的示例代碼,如下:


function multiple_threads_request($nodes){ 
        $mh = curl_multi_init(); 
        $curl_array = array(); 
        foreach($nodes as $i => $url) 
        { 
            $curl_array[$i] = curl_init($url); 
            curl_setopt($curl_array[$i], CURLOPT_RETURNTRANSFER, true); 
            curl_multi_add_handle($mh, $curl_array[$i]); 
        } 
        $running = NULL; 
        do { 
            usleep(10000); 
            curl_multi_exec($mh,$running); 
        } while($running > 0); 
         
        $res = array(); 
        foreach($nodes as $i => $url) 
        { 
            $res[$url] = curl_multi_getcontent($curl_array[$i]); 
        } 
         
        foreach($nodes as $i => $url){ 
            curl_multi_remove_handle($mh, $curl_array[$i]); 
        } 
        curl_multi_close($mh);        
        return $res; 
} 
print_r(muti_thread_request(array( 
    'http://www.example.com', 
    'http://www.example.net', 
)));

下面是邊請求url,邊處理返回數據的示例代碼:


/*
 * @purpose: 使用curl並行處理url
 * @return: array 每個url獲取的數據
 * @param: $urls array url列表
 * @param: $callback string 需要進行內容處理的回調函數。示例:func(array)
 */
function curl($urls = array(), $callback = '')
{
    $response = array();
    if (empty($urls)) {
        return $response;
    }
    $chs = curl_multi_init();
    $map = array();
    foreach($urls as $url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_multi_add_handle($chs, $ch);
        $map[strval($ch)] = $url;
    }
    do{
        if (($status = curl_multi_exec($chs, $active)) != CURLM_CALL_MULTI_PERFORM) {
            if ($status != CURLM_OK) { break; } //如果沒有準備就緒,就再次調用curl_multi_exec
            while ($done = curl_multi_info_read($chs)) {
                $info = curl_getinfo($done["handle"]);
                $error = curl_error($done["handle"]);
                $result = curl_multi_getcontent($done["handle"]);
                $url = $map[strval($done["handle"])];
                $rtn = compact('info', 'error', 'result', 'url');
                if (trim($callback)) {
                    $callback($rtn);
                }
                $response[$url] = $rtn;
                curl_multi_remove_handle($chs, $done['handle']);
                curl_close($done['handle']);
                //如果仍然有未處理完畢的句柄,那麼就select
                if ($active > 0) {
                    curl_multi_select($chs, 0.5); //此處會導致阻塞大概0.5秒。
                }
            }
        }
    }
    while($active > 0); //還有句柄處理還在進行中
    curl_multi_close($chs);
    return $response;
}
 
//使用方法
function deal($data){
    if ($data["error"] == '') {
        echo $data["url"]." -- ".$data["info"]["http_code"]."\n";
    } else {
        echo $data["url"]." -- ".$data["error"]."\n";
    }
}
$urls = array();
for ($i = 0; $i < 10; $i++) {
    $urls[] = 'http://www.baidu.com/s?wd=etao_'.$i;
    $urls[] = 'http://www.so.com/s?q=etao_'.$i;
    $urls[] = 'http://www.soso.com/q?w=etao_'.$i;
}
curl($urls, "deal"); 

演示網址 http://demo.bo56.com/bigpipe/

註釋:
1.關於curl_multi_exec函數的返回值:
返回CURLM_CALL_MULTI_PERFORM 說明curl_multi_exec需要馬上被再調用一次。
返回CURLM_OK 說明已經有需要處理的數據。這時你需要進行相關處理,處理完後再次調用curl_multi_exec。
php中的curl_multi_exec是調用的curl庫中的curl_multi_perform方法。代碼在multi.c的230行左右。

2.此方式,雖然在獲取數據和數據處理上是並行的,但是在數據處理時依然是串行的。即數據是一條條依次處理的。如果deal方法比較耗時的話,那整體會非常耗時。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章