PHP CURL 實現併發訪問網絡資源


 原文發表在http://www.trackself.com/archives/463.html
  1. PHP cURL 所有函數列表:

  2. http://php.net/manual/zh/ref.curl.php



以下是PHP中cURL多線程相關函數:

curl_multi_add_handle — 向curl批處理會話中添加單獨的curl句柄
curl_multi_close — 關閉一組cURL句柄
curl_multi_exec — 運行當前 cURL 句柄的子連接
curl_multi_getcontent — 如果設置了CURLOPT_RETURNTRANSFER,則返回獲取的輸出的文本流
curl_multi_info_read — 獲取當前解析的cURL的相關傳輸信息
curl_multi_init — 返回一個新cURL批處理句柄
curl_multi_remove_handle — 移除curl批處理句柄資源中的某個句柄資源
curl_multi_select — 等待所有cURL批處理中的活動連接
curl_multi_setopt — 爲 cURL 並行處理設置一個選項
curl_multi_strerror — Return string describing error code

一般來說,想到要用這些函數時,目的顯然應該是要同時請求多個URL,而不是一個一個依次請求,否則不如自己循環去調curl_exec好了。

步驟總結如下:

1、調用 curl_multi_init,初始化一個批處理handle
2、循環調用 curl_multi_add_handle,往1中的批處理handle 添加curl_init來的子handle
3、持續調用 curl_multi_exec,直到所有子handle執行完畢。
4、根據需要循環調用 curl_multi_getcontent 獲取結果
5、調用 curl_multi_remove_handle,併爲每個字handle調用curl_close
6、調用 curl_multi_close

<?php

/*
  cmi該函數的目的在於併發請求多個url,然後返回http://www.trackself.com編寫,真正的PHP併發
  原文發表在http://www.trackself.com/archives/463.html
  此併發請求在url多於2的時候,明顯比for ... file_get_contents ...要優很多
  核心是curl庫的curl_multi方法
  用法:
  $urls=array(
  'http://www.google.com',
  'http://www.baidu.com',
  'http://sina.com',
  'http://163.com'
  )
  $htmls=cmi($urls);
  print_r($htmls);

  //傳入的$connomains是URL一維數組,由http://www.trackself.com編寫
  //該函數的目的在於併發請求多個url,然後返回源碼
  //以一次性略增加CPU爲代價
  //來減輕服務器因爲for ... file_get_contents ...的長時連接負擔及內存和CPU的負擔,所以併發數不要大多(50以內效果非常好),儘量不要用於單頁面或3頁面以內的請求
  //$killspace爲真時表示自動去掉HTML中換行及多餘的空白,$forhtml爲真時表示反回源碼,爲faluse時就是併發執行請求了(可以用於計劃任務)
  //後面的幾個參數的詳細說明請看註釋,畢竟函數不長
 */

function cmi($connomains, $killspace = TRUE, $forhtml = TRUE, $timeout = 6, $header = 0, $follow = 1) {


    $res = array();
    $urlsa = array();
    $results = array();
    $mh = curl_multi_init(); //創建多curl對象,爲了幾乎同時執行
    foreach ($connomains as $i => $url) {
        $conn[$i] = curl_init($url); //若url中含有gb2312漢字,例如FTP時,要在傳入url的時候處理一下,這裏不用
        curl_setopt($conn[$i], CURLOPT_TIMEOUT, $timeout); //此時間須根據頁面的HTML源碼出來的時間,一般是在1s內的,慢的話應該也不會6秒,極慢則是在16秒內
        curl_setopt($conn[$i], CURLOPT_HEADER, $header); //不返回請求頭,只要源碼
        curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1); //必須爲1
        curl_setopt($conn[$i], CURLOPT_FOLLOWLOCATION, $follow); //如果頁面含有自動跳轉的代碼如301或者302HTTP時,自動拿轉向的頁面
        curl_multi_add_handle($mh, $conn[$i]); //關鍵,一定要放在上面幾句之下,將單curl對象賦給多對象
    }
    //下面一大步的目的是爲了減少cpu的無謂負擔,暫時不明,來自php.net的建議,幾乎是固定用法
    do {
        $mrc = curl_multi_exec($mh, $active); //當無數據時或請求暫停時,active=true
    } while ($mrc == CURLM_CALL_MULTI_PERFORM); //當正在接受數據時
    while ($active and $mrc == CURLM_OK) {//當無數據時或請求暫停時,active=true,爲了減少cpu的無謂負擔,這一步很難明啊
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    /////////////////////////////////////////////////////////////////////////////////////////
//下面返回結果
    foreach ($connomains as $i => $url) {
        $cinfo = curl_getinfo($conn[$i]); //可用於取得一些有用的參數,可以認爲是header
        $url = $cinfo[url]; //真實url,有些url
        if ($killspace) {//有點水消耗
            $str = trim(curl_multi_getcontent($conn[$i]));
            $str = preg_replace('/\s(?=\s)/', '', $str); //去掉跟隨別的擠在一塊的空白       
            $str = preg_replace('/[\n\r\t]/', ' ', $str);  //最後,去掉非space 的空白,用一個空格代替
            $res[$i] = stripslashes($str); //取得對象源碼,並取消換行,節約內存的同時,可以方便作正則處理
        } else {
            $res[$i] = curl_multi_getcontent($conn[$i]);
        }
        if (!$forhtml) {//節約內存
            $res[$i] = NULL;
        }
        /* 下面這一段放一些高消耗的程序代碼,用來處理HTML,我保留的一句=NULL是要提醒,
         * 及時清空對象釋放內存,此程序在併發過程中如果源碼太大,內在消耗嚴重
          事實上,這裏應該做一個callback函數或者你應該將你的邏輯直接放到這裏來,我爲了程序可重複,沒這麼做
          preg_match_all($preg,$res[$i],$matchlinks);
          $res[$i]=NULL;
         */
        curl_close($conn[$i]); //關閉所有對象
        curl_multi_remove_handle($mh, $conn[$i]);   //用完馬上釋放資源              
    }
    curl_multi_close($mh);
    $mh = NULL;
    $conn = NULL;

    return $res;
}

//cmi 下面是版本二,幾乎一至的代碼,但輸出的形式改爲爲帶key;
/*
  cmi該函數的目的在於併發請求多個url,然後返回http://www.trackself.com編寫,真正的PHP併發
  原文發表在http://www.trackself.com/archives/463.html
  此併發請求在url多於2的時候,明顯比for ... file_get_contents ...要優很多
  核心是curl庫的curl_multi方法
  用法:
  //array_flip(array_flip($connomains))
  $urls=array(
  'http://www.google.com',
  'http://www.baidu.com',
  'http://sina.com',
  'http://163.com'
  )
  $urls=array_flip(array_flip($connomains));//去除url中的重複的項,注意傳入urls時一定要系合法的url表達,雖然不會影響其它url執行,但會減慢執行速度
  $htmls=cmi($urls);
  print_r($htmls);

  //傳入的$connomains是URL一維數組,由http://www.trackself.com編寫
  //該函數的目的在於併發請求多個url,然後返回源碼
  //以一次性略增加CPU爲代價
  //來減輕服務器因爲for ... file_get_contents ...的長時連接負擔及內存和CPU的負擔,所以併發數不要大多(50以內效果非常好),儘量不要用於單頁面或3頁面以內的請求
  //$killspace爲真時表示自動去掉HTML中換行及多餘的空白,$forhtml爲真時表示反回源碼,爲faluse時就是併發執行請求了(可以用於計劃任務)
  //後面的幾個參數的詳細說明請看註釋,畢竟函數不長
 */
function cmi($connomains, $killspace = TRUE, $forhtml = TRUE, $timeout = 6, $header = 0, $follow = 1) {


    $res = array(); //用於保存結果     
//$connomains=array_flip(array_flip($connomains));//去除url中的重複項
    $mh = curl_multi_init(); //創建多curl對象,爲了幾乎同時執行
    foreach ($connomains as $i => $url) {
        $conn[$url] = curl_init($url); //若url中含有gb2312漢字,例如FTP時,要在傳入url的時候處理一下,這裏不用
        //此時間須根據頁面的HTML源碼出來的時間,一般是在1s內的,慢的話應該也不會6秒,極慢則是在16秒內
        curl_setopt($conn[$url], CURLOPT_TIMEOUT, $timeout); 
        curl_setopt($conn[$url], CURLOPT_HEADER, $header); //不返回請求頭,只要源碼
        curl_setopt($conn[$url], CURLOPT_RETURNTRANSFER, 1); //必須爲1
        //如果頁面含有自動跳轉的代碼如301或者302HTTP時,自動拿轉向的頁面
        curl_setopt($conn[$url], CURLOPT_FOLLOWLOCATION, $follow); 
        curl_multi_add_handle($mh, $conn[$url]); //關鍵,一定要放在上面幾句之下,將單curl對象賦給多對象
    }
    //下面一大步的目的是爲了減少cpu的無謂負擔,暫時不明,來自php.net的建議,幾乎是固定用法
    do {
        $mrc = curl_multi_exec($mh, $active); //當無數據時或請求暫停時,active=true
    } while ($mrc == CURLM_CALL_MULTI_PERFORM); //當正在接受數據時
    while ($active and $mrc == CURLM_OK) {//當無數據時或請求暫停時,active=true,爲了減少cpu的無謂負擔,這一步很難明啊
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    /////////////////////////////////////////////////////////////////////////////////////////
//下面返回結果
    foreach ($connomains as $i => $url) {
        $cinfo = curl_getinfo($conn[$url]); //可用於取得一些有用的參數,可以認爲是header
        //$url=$cinfo[url];//真實url,有些url
        if ($killspace) {//有點水消耗
            $str = trim(curl_multi_getcontent($conn[$url]));
            $str = preg_replace('/\s(?=\s)/', '', $str); //去掉跟隨別的擠在一塊的空白       
            $str = preg_replace('/[\n\r\t]/', ' ', $str);  //最後,去掉非space 的空白,用一個空格代替
            $res[$url] = stripslashes($str); //取得對象源碼,並取消換行,節約內存的同時,可以方便作正則處理
        } else {
            $res[$url] = curl_multi_getcontent($conn[$url]);
        }
        if (!$forhtml) {//節約內存
            $res[$url] = NULL;
        }
        /* 下面這一段放一些高消耗的程序代碼,用來處理HTML,我保留的一句=NULL是要提醒,
          及時清空對象釋放內存,此程序在併發過程中如果源碼太大,內在消耗嚴重
          //事實上,這裏應該做一個callback函數或者你應該將你的邏輯直接放到這裏來,我爲了程序可重複,沒這麼做
          preg_match_all($preg,$res[$i],$matchlinks);
          $res[$i]=NULL;
         */
        curl_close($conn[$url]); //關閉所有對象 
        curl_multi_remove_handle($mh, $conn[$url]);   //用完馬上釋放資源              
    }
    curl_multi_close($mh);
    $mh = NULL;
    $conn = NULL;
    $connomains = NULL;

    return $res;
}


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