PHP多進程(4) :內部多進程

來源:互聯網
上載者:User
上面一個系列的教程:

PHP多進程編程(一)

PHP多進程編程(二)管道通訊

PHP多進程編程(三)多進程抓取網頁的示範

說的都是只相容unix 伺服器的多進程,下面來講講在window 和 unix 都相容的多進程(這裡是泛指,下面的curl實際上是通過IO複用實現的)。

通過擴充實現多線程的典型例子是CURL,CURL 支援多線程的抓取網頁的功能。

這部分過於抽象,所以,我先給出一個CURL並行抓取多個網頁內容的一個分裝類。這個類實際上很實用,

詳細分析這些函數的內部實現將在下一個教程裡面描述。

你可能不能很好的理解這個類,而且,php curl 官方首頁上都有很多錯誤的例子,在講述了其內部機制

後,你就能夠明白了。

先看代碼:

 class Http_MultiRequest{    //要並行抓取的url 列表    private $urls = array();    //curl 的選項    private $options;    //建構函式    function __construct($options = array())    {        $this->setOptions($options);    }    //設定url 列表    function setUrls($urls)    {        $this->urls = $urls;        return $this;    }    //設定選項    function setOptions($options)    {        $options[CURLOPT_RETURNTRANSFER] = 1;        if (isset($options['HTTP_POST']))         {            curl_setopt($ch, CURLOPT_POST, 1);            curl_setopt($ch, CURLOPT_POSTFIELDS, $options['HTTP_POST']);            unset($options['HTTP_POST']);        }        if (!isset($options[CURLOPT_USERAGENT]))         {            $options[CURLOPT_USERAGENT] = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)';        }        if (!isset($options[CURLOPT_FOLLOWLOCATION]))         {            $options[CURLOPT_FOLLOWLOCATION] = 1;        }        if (!isset($options[CURLOPT_HEADER]))        {            $options[CURLOPT_HEADER] = 0;        }        $this->options = $options;    }    //並行抓取所有的內容    function exec()    {        if(empty($this->urls) || !is_array($this->urls))        {            return false;        }        $curl = $data = array();        $mh = curl_multi_init();        foreach($this->urls as $k => $v)        {            $curl[$k] = $this->addHandle($mh, $v);        }        $this->execMulitHandle($mh);        foreach($this->urls as $k => $v)        {            $data[$k] = curl_multi_getcontent($curl[$k]);            curl_multi_remove_handle($mh, $curl[$k]);        }        curl_multi_close($mh);        return $data;    }    //只抓取一個網頁的內容。    function execOne($url)    {        if (empty($url)) {            return false;        }        $ch = curl_init($url);        $this->setOneOption($ch);        $content = curl_exec($ch);        curl_close($ch);        return $content;    }    //內建函式,設定某個handle 的選項    private function setOneOption($ch)    {        curl_setopt_array($ch, $this->options);    }    //添加一個新的並行抓取 handle    private function addHandle($mh, $url)    {        $ch = curl_init($url);        $this->setOneOption($ch);        curl_multi_add_handle($mh, $ch);        return $ch;    }    //並存執行(這樣的寫法是一個常見的錯誤,我這裡還是採用這樣的寫法,這個寫法    //下載一個小檔案都可能導致cup佔用100%, 並且,這個迴圈會運行10萬次以上    //這是一個典型的不懂原理產生的錯誤。這個錯誤在PHP官方的文檔上都相當的常見。)    private function execMulitHandle2($mh)    {        $i = 0;        $running = null;        do {            curl_multi_exec($mh, $running);            $i++;        } while ($running > 0);        //var_dump($i);    }    //應該用這樣的寫法    private function execMulitHandle($mh)    {        $i = 0;        do {$mrc = curl_multi_exec($mh,$active); $i++;} while ($mrc == CURLM_CALL_MULTI_PERFORM);        while ($active && $mrc == CURLM_OK)         {            if (curl_multi_select($mh) != -1)            {                do {$mrc = curl_multi_exec($mh, $active); $i++;} while ($mrc == CURLM_CALL_MULTI_PERFORM);            }            $i++;        }        //var_dump($i);    }}

看最後一個注釋最多的函數,這個錯誤在平時調試的時候可能不太容易發現,因為程式完全正常,但是,在生產伺服器下,馬上會引起崩潰效果。

解釋為什麼不能這樣,必須從C 語言內部實現的角度來分析。這個部分將放到下一個教程(PHP進階編程之–單線程實現並行抓取網頁 )。不過不是通過C語言來表述原理,而是通過PHP

這個類,實際上也就很簡單的實現了前面我們費了4個教程的篇幅,並且是九牛二虎之力才實現的多線程的抓取網頁的功能。在純PHP的實現下,我們只能用一個後台服務的方式來比較好的實現,但是當你使用 作業系統介面語言 C 語言時候,這個實現當然就更加的簡單,靈活,高效。

就同時抓取幾個網頁這樣一件簡單的事情,實際上在底層涉及到了很多東西,對很多半路出家的PHP程式員,可能不喜歡談多線程這個東西,深入了就涉及到作業系統,淺點說就是並行運行好幾個“程式”。但是,很多時候,多線程必不可少,比如要寫個快點的爬蟲,往往就會浪費九牛二虎之力。不過,PHP的程式員現在應該感謝CURL 這個擴充,這樣,你完全不需要用你不太精通的 python 去寫爬蟲了,對於一個中型大小的爬蟲,有這個內部多線程,就已經足夠了。

最後是上面的類的一個測試的例子:

$urls = array("http://baidu.com", "http://baidu.com", "http://baidu.com", "http://baidu.com", "http://baidu.com", "http://baidu.com", "http://www.google.com", "http://www.sina.com.cn", );$m = new Http_MultiRequest();$t = microtime(true);$m->setUrls($urls);//parallel fetch(並行抓取):$data = $m->exec();$parallel_time = microtime(true) - $t;echo $parallel_time . "\n";$t = microtime(true);//serial fetch(串列抓取):foreach ($urls as $url){    $data[] = $m->execOne($url);}$serial_time = microtime(true) - $t;echo $serial_time . "\n";
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.