php中curl同時發送多個請求curl_multi函數集的用法
一、引言
這段時間比較忙,已經很久沒有寫博客了。今天我就來聊聊我關於curl_multi_*函數集的使用心得,關於http請求的問題。
當我們用戶php發起一個http請求的時候。我們會首先想到用什麼?沒錯,我們會創建curl來請求。當我們在一次執行中需要發起多個http請求呢。這簡單,對每一個URL發起一次url請求。請求玩第1個再請求第2個….這就完了?哪我們還說個啥。
官網鏈接: http://php.net/manual/zh/book.curl.php
二、多次簡單的curl請求弊端
圖1.串列請求與並行請求
我們舉個栗子。現在有三個http請求。每個請求耗時2s。如果按照簡單的curl請求(圖1-(1))。耗時6s.這是不能容忍的。如果請求的個數越多耗時約多。
有沒有一種方式來縮小查詢時間?能不能三個http請求同時執行(如圖1-(1))?有很多方法來解決這個問題,將耗時減少到2s。如:多進程、線程、事件循環、curl_multi_*等等。最簡單的方式就是通過curl_multi_*函數來完成。事實上curl_multi_*內部實現就是用的事件循環。
三、簡單的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 "一個請求下載完成!
";
// 把請求已經完成了得 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// 下載完畢,關閉下載器
curl_multi_close($downloader);
echo 「所有請求下載完成!」;
在該例子中,首先創建三個或多個要請求的url請求對象。通過curl_multi_*函數創建下載器。將請求寫入下載器中。最後輪詢。等待三個請求現在完成。做處理。
四、複雜的curl_multi_*運用
這就是curl_multi_*用法?too yong too simple!在上面的例子中。下載器$downloader中的請求是一開始就添加好了的。我們能不能動態的向下載器中添加請求。動態的從下載器中取出已經完成了的請求。想想。這是什麼?這不就是爬蟲的核心部分-動態下載器。如何動態的添加?我們可以用多進程通過IPC添加。我們可以通過協程通過隊列添加等待。
我這實現了一個通過協程+curl_multi_*的爬蟲框架。
Tspider: https://github.com/hirudy/Tspider 。
單進程可處理請求2000-5000/min。
打開今日頭條,查看更多精彩圖片※「轉載」一個大數據屌絲的一天
※基於elasticsearch6.2.3版本自定義插件開發遇到的問題總結
TAG:程序員小新人學習 |