php中curl_multi函數集的用法

1、引言

這段時間比較忙,已經好久沒有寫博客了。今天我就來聊聊我關於curl_multi_*函數集的使用心得,關於http請求的問題。php

當咱們用戶php發起一個http請求的時候。咱們會首先想到用什麼?沒錯,咱們會建立curl來請求。當咱們在一次執行中須要發起多個http請求呢。這簡單,對每個URL發起一次url請求。請求玩第1個再請求第2個....這就完了?哪咱們還說個啥。git

官網連接:http://php.net/manual/zh/book.curl.phpgithub

2、屢次簡單的curl請求弊端

圖1.串行請求與並行請求

咱們舉個栗子。如今有三個http請求。每一個請求耗時2s。若是按照簡單的curl請求(圖1-(1))。耗時6s.這是不能容忍的。若是請求的個數越多耗時約多。框架

有沒有一種方式來縮小查詢時間?能不能三個http請求同時執行(如圖1-(1))?有不少方法來解決這個問題,將耗時減小到2s。如:多進程、線程、事件循環、curl_multi_*等等。最簡單的方式就是經過curl_multi_*函數來完成。事實上curl_multi_*內部實現就是用的事件循環。curl

3、簡單的curl_multi_*運用

<?php
/**
 *
 * curl_multi_*簡單運用
 *
 * @author: rudy
 * @date: 2016/07/12
 */

/**
 * 根據url,postData獲取curl請求對象,這個比較簡單,能夠看官方文檔
 */
function getCurlObject($url,$postData=array(),$header=array()){
    $options = array();
    $url = trim($url);
    $options[CURLOPT_URL] = $url;
    $options[CURLOPT_TIMEOUT] = 10;
    $options[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36';
    $options[CURLOPT_RETURNTRANSFER] = true;
    //    $options[CURLOPT_PROXY] = '127.0.0.1:8888';
    foreach($header as $key=>$value){
        $options[$key] =$value;
    }
    if(!empty($postData) && is_array($postData)){
        $options[CURLOPT_POST] = true;
        $options[CURLOPT_POSTFIELDS] = http_build_query($postData);
    }
    if(stripos($url,'https') === 0){
        $options[CURLOPT_SSL_VERIFYPEER] = false;
    }
    $ch = curl_init();
    curl_setopt_array($ch,$options);

    return $ch;
}

// 建立三個待請求的url對象
$chList = array();
$chList[] = getCurlObject('https://www.baidu.com');
$chList[] = getCurlObject('http://www.jd.com');
$chList[] = getCurlObject('http://www.jianshu.com/');

// 建立多請求執行對象
$downloader = curl_multi_init();

// 將三個待請求對象放入下載器中
foreach ($chList as $ch){
    curl_multi_add_handle($downloader,$ch);
}

// 輪詢
do {
    while (($execrun = curl_multi_exec($downloader, $running)) == CURLM_CALL_MULTI_PERFORM) ;
    if ($execrun != CURLM_OK) {
        break;
    }

    // 一旦有一個請求完成,找出來,處理,由於curl底層是select,因此最大受限於1024
    while ($done = curl_multi_info_read($downloader))
    {
        // 從請求中獲取信息、內容、錯誤
        $info = curl_getinfo($done['handle']);
        $output = curl_multi_getcontent($done['handle']);
        $error = curl_error($done['handle']);

        // 將請求結果保存,我這裏是打印出來
        print $output;
//        print "一個請求下載完成!\n";

        // 把請求已經完成了得 curl handle 刪除
        curl_multi_remove_handle($downloader, $done['handle']);
    }

    // 當沒有數據的時候進行堵塞,把 CPU 使用權交出來,避免上面 do 死循環空跑數據致使 CPU 100%
    if ($running) {
        $rel = curl_multi_select($downloader, 1);
        if($rel == -1){
            usleep(1000);
        }
    }

    if( $running == false){
        break;
    }
} while (true);

// 下載完畢,關閉下載器
curl_multi_close($downloader);
echo "全部請求下載完成!";

在該例子中,首先建立三個或多個要請求的url請求對象。經過curl_multi_*函數建立下載器。將請求寫入下載器中。最後輪詢。等待三個請求如今完成。作處理。ide

4、複雜的curl_multi_*運用

這就是curl_multi_*用法?too yong too simple!在上面的例子中。下載器$downloader中的請求是一開始就添加好了的。咱們能不能動態的向下載器中添加請求。動態的從下載器中取出已經完成了的請求。想一想。這是什麼?這不就是爬蟲的核心部分-動態下載器。如何動態的添加?咱們能夠用多進程經過IPC添加。咱們能夠經過協程經過隊列添加等待。函數

  1. curl_multi_*函數實現的HTTP壓測工具:
    https://github.com/hirudy/phplib/blob/master/phpAb.php工具

  2. curl_multi_*實現的http請求類:post

https://github.com/hirudy/phplibui

  1. 我這實現了一個經過協程+curl_multi_*的爬蟲框架。

Tspider:https://github.com/hirudy/Tspider。單進程可處理請求2000-5000/min。

相關文章
相關標籤/搜索