成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

PHP實(shí)現(xiàn)并發(fā)請(qǐng)求

zhangfaliang / 2142人閱讀

摘要:不支持多線程模式和回調(diào)處理,因此內(nèi)部腳本都是同步阻塞式的,如果你發(fā)起一個(gè)的請(qǐng)求,那么程序就會(huì)阻塞,直到請(qǐng)求返回結(jié)果,才會(huì)繼續(xù)執(zhí)行代碼。參考資料手冊(cè)手冊(cè)預(yù)定義常量中實(shí)現(xiàn)多線程請(qǐng)求詳解每次使用同時(shí)并發(fā)多少請(qǐng)求合適簡(jiǎn)書多線程及原理

后端服務(wù)開發(fā)中經(jīng)常會(huì)有并發(fā)請(qǐng)求的需求,比如你需要獲取10家供應(yīng)商的帶寬數(shù)據(jù)(每個(gè)都提供不同的url),然后返回一個(gè)整合后的數(shù)據(jù),你會(huì)怎么做呢?

PHP中,最直觀的做法foreach遍歷urls,并保存每個(gè)請(qǐng)求的結(jié)果即可,那么如果供應(yīng)商提供的接口平均耗時(shí)5s,你的這個(gè)接口請(qǐng)求耗時(shí)就達(dá)到了50s,這對(duì)于追求速度和性能的網(wǎng)站來說是不可接受的。

這個(gè)時(shí)候你就需要并發(fā)請(qǐng)求了。

PHP請(qǐng)求

PHP是單進(jìn)程同步模型,一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)進(jìn)程,I/O是同步阻塞的。通過nginx/apache/php-fpm等服務(wù)的擴(kuò)展,才使得PHP提供高并發(fā)的服務(wù),原理就是維護(hù)一個(gè)進(jìn)程池,每個(gè)請(qǐng)求服務(wù)時(shí)多帶帶起一個(gè)新的進(jìn)程,每個(gè)進(jìn)程獨(dú)立存在。

PHP不支持多線程模式和回調(diào)處理,因此PHP內(nèi)部腳本都是同步阻塞式的,如果你發(fā)起一個(gè)5s的請(qǐng)求,那么程序就會(huì)I/O阻塞5s,直到請(qǐng)求返回結(jié)果,才會(huì)繼續(xù)執(zhí)行代碼。因此做爬蟲之類的高并發(fā)請(qǐng)求需求很吃力。

那怎么來解決并發(fā)請(qǐng)求的問題呢?除了內(nèi)置的file_get_contentsfsockopen請(qǐng)求方式,PHP也支持cURL擴(kuò)展來發(fā)起請(qǐng)求,它支持常規(guī)的單個(gè)請(qǐng)求:PHP cURL請(qǐng)求詳解,也支持并發(fā)請(qǐng)求,其并發(fā)原理是cURL擴(kuò)展使用多線程來管理多請(qǐng)求。

PHP并發(fā)請(qǐng)求

我們直接來看代碼demo:

// 簡(jiǎn)單demo,默認(rèn)支持為GET請(qǐng)求
public function multiRequest($urls) {
    $mh = curl_multi_init();
    $urlHandlers = [];
    $urlData = [];
    // 初始化多個(gè)請(qǐng)求句柄為一個(gè)
    foreach($urls as $value) {
        $ch = curl_init();
        $url = $value["url"];
        $url .= strpos($url, "?") ? "&" : "?";
        $params = $value["params"];
        $url .= is_array($params) ? http_build_query($params) : $params;
        curl_setopt($ch, CURLOPT_URL, $url);
        // 設(shè)置數(shù)據(jù)通過字符串返回,而不是直接輸出
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $urlHandlers[] = $ch;
        curl_multi_add_handle($mh, $ch);
    }
    $active = null;
    // 檢測(cè)操作的初始狀態(tài)是否OK,CURLM_CALL_MULTI_PERFORM為常量值-1
    do {
        // 返回的$active是活躍連接的數(shù)量,$mrc是返回值,正常為0,異常為-1
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    // 如果還有活動(dòng)的請(qǐng)求,同時(shí)操作狀態(tài)OK,CURLM_OK為常量值0
    while ($active && $mrc == CURLM_OK) {
        // 持續(xù)查詢狀態(tài)并不利于處理任務(wù),每50ms檢查一次,此時(shí)釋放CPU,降低機(jī)器負(fù)載
        usleep(50000);
        // 如果批處理句柄OK,重復(fù)檢查操作狀態(tài)直至OK。select返回值異常時(shí)為-1,正常為1(因?yàn)橹挥?個(gè)批處理句柄)
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    // 獲取返回結(jié)果
    foreach($urlHandlers as $index => $ch) {
        $urlData[$index] = curl_multi_getcontent($ch);
        // 移除單個(gè)curl句柄
        curl_multi_remove_handle($mh, $ch);
    }
    curl_multi_close($mh);
    return $urlData;
}

在該并發(fā)請(qǐng)求中,先創(chuàng)建一個(gè)批處理句柄,然后將urlcURL句柄添加到批處理句柄中,并不斷查詢批處理句柄的執(zhí)行狀態(tài),當(dāng)執(zhí)行完成后,獲取返回的結(jié)果。

curl_multi 相關(guān)函數(shù)
/** 函數(shù)作用:返回一個(gè)新cURL批處理句柄
    @return resource 成功返回cURL批處理句柄,失敗返回false
*/
resource curl_multi_init ( void )

/** 函數(shù)作用:向curl批處理會(huì)話中添加多帶帶的curl句柄
    @param $mh 由curl_multi_init返回的批處理句柄
    @param $ch 由curl_init返回的cURL句柄
    @return resource 成功返回cURL批處理句柄,失敗返回false
*/
int curl_multi_add_handle ( resource $mh , resource $ch )

/** 函數(shù)作用:運(yùn)行當(dāng)前 cURL 句柄的子連接
    @param $mh 由curl_multi_init返回的批處理句柄
    @param $still_running 一個(gè)用來判斷操作是否仍在執(zhí)行的標(biāo)識(shí)的引用
    @return 一個(gè)定義于 cURL 預(yù)定義常量中的 cURL 代碼
*/
int curl_multi_exec ( resource $mh , int &$still_running )

/** 函數(shù)作用:等待所有cURL批處理中的活動(dòng)連接
    @param $mh 由curl_multi_init返回的批處理句柄
    @param $timeout 以秒為單位,等待響應(yīng)的時(shí)間
    @return 成功時(shí)返回描述符集合中描述符的數(shù)量。失敗時(shí),select失敗時(shí)返回-1,否則返回超時(shí)(從底層的select系統(tǒng)調(diào)用).
*/
int curl_multi_select ( resource $mh [, float $timeout = 1.0 ] )

/** 函數(shù)作用:移除cURL批處理句柄資源中的某個(gè)句柄資源
    說明:從給定的批處理句柄mh中移除ch句柄。當(dāng)ch句柄被移除以后,仍然可以合法地用curl_exec()執(zhí)行這個(gè)句柄。如果要移除的句柄正在被使用,則這個(gè)句柄涉及的所有傳輸任務(wù)會(huì)被中止。
    @param $mh 由curl_multi_init返回的批處理句柄
    @param $ch 由curl_init返回的cURL句柄
    @return 成功時(shí)返回0,失敗時(shí)返回CURLM_XXX中的一個(gè)
*/
int curl_multi_remove_handle ( resource $mh , resource $ch )

/** 函數(shù)作用:關(guān)閉一組cURL句柄
    @param $mh 由curl_multi_init返回的批處理句柄
    @return void
*/
void curl_multi_close ( resource $mh )

/** 函數(shù)作用:如果設(shè)置了CURLOPT_RETURNTRANSFER,則返回獲取的輸出的文本流
    @param $ch 由curl_init返回的cURL句柄
    @return string 如果設(shè)置了CURLOPT_RETURNTRANSFER,則返回獲取的輸出的文本流。
*/
string curl_multi_getcontent ( resource $ch )
本例中使用到的預(yù)定義常量:
CURLM_CALL_MULTI_PERFORM: (int) -1
CURLM_OK: (int) 0
PHP并發(fā)請(qǐng)求耗時(shí)對(duì)比

第一次請(qǐng)求使用上面的curl_multi_init方法,并發(fā)請(qǐng)求105次。

第二次請(qǐng)求使用傳統(tǒng)的foreach方法,遍歷105次使用curl_init方法請(qǐng)求。

實(shí)際的請(qǐng)求耗時(shí)結(jié)果為:

刨除download的約765ms耗時(shí),單純的請(qǐng)求耗時(shí)優(yōu)化達(dá)到了39.83/1.58達(dá)到了25倍,如果繼續(xù)刨除建連相關(guān)的耗時(shí),應(yīng)該會(huì)更高。這其中的耗時(shí):

方案1:最慢的一個(gè)接口達(dá)到了1.58s

方案2:105個(gè)接口的平均耗時(shí)是384ms

這個(gè)測(cè)試的請(qǐng)求是我的環(huán)境的內(nèi)部接口,所以耗時(shí)很短,實(shí)際爬蟲請(qǐng)求環(huán)境優(yōu)化會(huì)更明顯。
注意項(xiàng) 并發(fā)數(shù)限制

curl_multi會(huì)消耗很多的系統(tǒng)資源,在并發(fā)請(qǐng)求時(shí)并發(fā)數(shù)有一定閾值,一般為512,是由于CURL內(nèi)部限制,超過最大并發(fā)會(huì)導(dǎo)致失敗。

在我做的測(cè)試中,發(fā)起2000個(gè)相同的請(qǐng)求,并輸出每一個(gè)請(qǐng)求的響應(yīng)結(jié)果。測(cè)試結(jié)果2000個(gè)請(qǐng)求共有366個(gè)成功,前331個(gè)均成功,在331-410次序之間共有35個(gè)成功的,第410個(gè)請(qǐng)求之后全部失敗。因此我們一定要注意并發(fā)數(shù)的限制,不要超過300個(gè),或者你可以自己在自己的機(jī)器上做一下測(cè)試,來制定你的閾值。

使用之前,請(qǐng)一定要注意并發(fā)數(shù)限制??!
超時(shí)時(shí)間

為了防止慢請(qǐng)求影響整個(gè)服務(wù),可以設(shè)置CURLOPT_TIMEOUT來控制超時(shí)時(shí)間,防止部分假死的請(qǐng)求無限阻塞進(jìn)程處理,最后打死機(jī)器服務(wù)。

CPU負(fù)載打滿

在代碼示例中,如果持續(xù)查詢并發(fā)的執(zhí)行狀態(tài),會(huì)導(dǎo)致cpu的負(fù)載過高,所以,需要在代碼里加上usleep(50000);的語句。
同時(shí),curl_multi_select也可以控制cpu占用,在數(shù)據(jù)有回應(yīng)前會(huì)一直處于等待狀態(tài),新數(shù)據(jù)一來就會(huì)被喚醒并繼續(xù)執(zhí)行,減少了CPU的無謂消耗。

參考資料

PHP手冊(cè) curl_multi_init:http://php.net/manual/zh/func...

PHP手冊(cè) curl預(yù)定義常量:http://php.net/manual/zh/curl...

PHP中foreach curl實(shí)現(xiàn)多線程:http://www.111cn.net/phper/ph...

Doing curl_multi_exec the right way:http://www.adrianworlddesign....

Segmentfault PHP cURL請(qǐng)求詳解:https://segmentfault.com/a/11...

CSDN 每次使用curl multi同時(shí)并發(fā)多少請(qǐng)求合適:https://blog.csdn.net/loophom...

簡(jiǎn)書 Curl多線程及原理:https://www.jianshu.com/p/f50...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30837.html

相關(guān)文章

  • 對(duì)PHP-FPM和CGI,還有并發(fā)響應(yīng)的理解

    摘要:官方對(duì)的解釋是進(jìn)程管理器。對(duì)并發(fā)訪問的處理進(jìn)程和線程從代碼級(jí)別來講不支持多線程操作,不能像等語言一樣可以編寫多線程代碼。 關(guān)于本篇文章的部分糾正,請(qǐng)參考這篇文章:http://www.cppblog.com/woaido... 首先搞清楚php-fpm與cgi的關(guān)系 CGI CGI是一個(gè)web server與cgi程序(這里可以理解為是php解釋器)之間進(jìn)行數(shù)據(jù)傳輸?shù)膮f(xié)議,保證了傳遞的...

    tianyu 評(píng)論0 收藏0
  • PHP編程中的并發(fā)

    摘要:編程中的并發(fā)周末去北京面了兩個(gè)公司,認(rèn)識(shí)了幾位技術(shù)牛人,面試中聊了很多,感覺收獲頗豐。本文大約總結(jié)了編程中的五種并發(fā)方式,最后的的實(shí)現(xiàn)純屬無聊,可以無視。生成的可以中斷函數(shù),并用向發(fā)送消息。 PHP編程中的并發(fā) 周末去北京面了兩個(gè)公司,認(rèn)識(shí)了幾位技術(shù)牛人,面試中聊了很多,感覺收獲頗豐。認(rèn)識(shí)到了自己的不足之處,也堅(jiān)定了自己對(duì)計(jì)算機(jī)學(xué)習(xí)的信心。本文是對(duì)其中一道面試題的總結(jié)。 面試中有一個(gè)問...

    lewinlee 評(píng)論0 收藏0
  • PHP并發(fā)IO編程之路

    摘要:下文如無特殊聲明將使用進(jìn)程同時(shí)表示進(jìn)程線程。收到數(shù)據(jù)后服務(wù)器程序進(jìn)行處理然后使用向客戶端發(fā)送響應(yīng)?,F(xiàn)在各種高并發(fā)異步的服務(wù)器程序都是基于實(shí)現(xiàn)的,比如。 并發(fā) IO 問題一直是服務(wù)器端編程中的技術(shù)難題,從最早的同步阻塞直接 Fork 進(jìn)程,到 Worker 進(jìn)程池/線程池,到現(xiàn)在的異步IO、協(xié)程。PHP 程序員因?yàn)橛袕?qiáng)大的 LAMP 框架,對(duì)這類底層方面的知識(shí)知之甚少,本文目的就是詳細(xì)介...

    Riddler 評(píng)論0 收藏0
  • php 并發(fā)控制中的獨(dú)占鎖

    并發(fā)大家都知道是什么情況,這里說的是并發(fā)多個(gè)請(qǐng)求搶占同一個(gè)資源,直接上實(shí)例吧 請(qǐng)求:index.php?mod=a&action=b&taskid=6處理: $key = a_b::.$uid._.$taskid; $v = $redis->get($key); if($v == 1){ $redis->setex($key,10,1); //處理邏輯省略 } 邏輯看來還可以,結(jié)果...

    yy13818512006 評(píng)論0 收藏0
  • php并發(fā)控制中的獨(dú)占鎖

    摘要:這中情況聽過很多,在開發(fā)過程中也沒刻意去模擬實(shí)驗(yàn)過。這就讓兩個(gè)以上的并發(fā)請(qǐng)求得到控制必須成功獲取鎖才能繼續(xù)。 1.并發(fā)問題并發(fā)大家都知道是什么情況,這里說的是并發(fā)多個(gè)請(qǐng)求搶占同一個(gè)資源,直接上實(shí)例吧 請(qǐng)求:index.php?mod=a&action=b&taskid=6處理: $key = a_b::.$uid._.$taskid; $v = $redis->get($key); i...

    AZmake 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<