iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 20
0
自我挑戰組

花式PHP系列 第 21

GuzzleHttp:併發HTTP REQUEST

read me senpai

在讀這篇文章之前,希望你至少已經:

  • 使用過 GuzzleHttp 進行 Http 請求

如果你有以下的知識儲備更好,但不強求:

概念

什麼情況會需要用它?

如果你在某段程式碼可能需要做不只一筆的 http 請求,
而且它們彼此之間沒有先後關係,那這東西就蠻適合你的~

可以在送出 request 之後,直接繼續執行嗎?

<?php

// pseudo code
$ids = range(1, 100);

$urls = array_map('generateUrl', $ids);

batchAsyncRequest($urls);

qk();
eatCookie();
drinkTea();
buyHouseAtEasternMarket();
buySaddleAtWesternMarket();

不行~
管你是想 qk,還是去西市買鞍韉都還是要等到 batchAsyncRequest() 跑完!
php 本身就沒有 async programming 這回事,
但要同時對不同 stream 操作還是辦得到的~[1]

用圖片解釋

「-」只是佔位符;「/」代表實際執行的時間

原本你可能是這樣的:

程式碼 時間軸
something() //////////----------------------------
request 1 ---------////------------------------
request 2 -------------////--------------------
request 3 -----------------////----------------
request 4 ---------------------////------------
request 5 -------------------------////--------
something() -----------------------------////////

調整後你其實是可以這樣的:

程式碼 時間軸
something() //////////------------
request 1 ---------////--------
request 2 ---------////--------
request 3 ---------////--------
request 4 ---------////--------
request 5 ---------////--------
something() -------------////////

語法

參考自官方文件

先從只有一筆請求的語法說起吧

<?php

use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;

$client = new GuzzleHttp\Client([
    'base_uri' => 'https://foo.com/api/'
]);

$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
    function (ResponseInterface $res) {
        echo $res->getStatusCode() . "\n";
    },
    function (RequestException $e) {
        echo $e->getMessage() . "\n";
        echo $e->getRequest()->getMethod();
    }
);

相信大家直到 $promise->then 之前大概都沒啥問題。

那麼,promise、then() 是什麼鬼呢?
promise 是普羅米修斯的近親嗎?

Promise

參考自 MDN 的說法,Promise 用來代表「異步操作回傳的結果」。

就像是如果你要透過 ajax 從你老闆那得到五串香蕉,
那老闆這邊就先給你一個 promise,這個 promise 代表我最後一定會給你點什麼

then()

在 js 裡面,
promise 的 then() 代表 promise 得到結果的時候要作的事情;
catch() 代表 promise 執行中發生錯誤時要作的處理。

不過在 GuzzleHttp 這邊,
$promise->then() 的第一個參數就是成功時要作的事情;
第二個參數就是發生錯誤時要作的處理(就是 catch())。

Promise 的狀態

這個是補充知識

一個 Promise 會有「擱置(pending)、實現(fulfilled)、拒絕(rejected)」三種狀態。

多筆請求

<?php

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client(['base_uri' => 'http://httpbin.org/']);

// 開啟多筆連線(這邊不會出現 blocking)
$promises = [
    'image' => $client->getAsync('/image'),
    'png'   => $client->getAsync('/image/png'),
    'jpeg'  => $client->getAsync('/image/jpeg'),
    'webp'  => $client->getAsync('/image/webp')
];

// 你可以執行這一行,以等待所有連線成功。但只要有一筆連線失敗,
// 就會丟出 ConnectException
$results = Promise\unwrap($promises);

// 你也可以執行這一行,以等待所有連線完成,即使它們有任何一筆失敗都沒關係。
$results = Promise\settle($promises)->wait();

// 在這邊你已經收到所有 response 了,你便能用 key 存取到對應的 response
echo $results['image']['value']->getHeader('Content-Length')[0]
echo $results['png']['value']->getHeader('Content-Length')[0]

限制併發的連線數量

如果你想要控制同時開啟的連線在一定的數量,
那麼你就需要參考官方文件的這個章節

其中的重點在原生的 yield 語法~
(return 一次之後該函式便暫停執行,直到下次執行這個函式)

除此之外大概大家也都沒什麼困難,
收工~

參考

  1. What is the point of Guzzle async promises?
  2. Left-align headers in markdown table?

上一篇
Eloquent:以withCount優化
下一篇
Carbon:setTestNow()輔助測試
系列文
花式PHP31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言