如何在PHP中做並發請求

Nick Zhang
5 min readJul 1, 2020

--

原生的PHP語言是單進程模式,一個請求對應一個進程,I/O是同步阻塞的,如果想要做到並發多進程的方式,就需要仰賴第三方套件,這次使用的套件是 guzzlehttp/guzzle ,這是一個PHP中很常使用做跨域請求的套件

安裝方式很簡單,只要使用 Composer 就可以了

composer require guzzlehttp/guzzle:^7.0

以下就寫個範例,測試一下如何做並發測試

可以寫個console command做並發請求發請源

$client = new Client();

// Initiate each request but do not block
$promises = [
'sleep_1' => $client->getAsync('http://localhost/sleep_1'),
'sleep_2' => $client->getAsync('http://localhost/sleep_2'),
'sleep_3' => $client->getAsync('http://localhost/sleep_3'),
'sleep_4' => $client->getAsync('http://localhost/sleep_4')
];

// Wait for the requests to complete; throws a ConnectException
// if any of the requests fail
$responses = Promise\unwrap($promises);

foreach ($responses as $response) {
dump($response->getBody()->getContents());
}

然後再另一端開啟服務做request的接收,這邊寫四個路由,分別sleep 1~4 秒,在回傳結果

// 路由1,暫停1秒
Route::get('sleep_1', function () {
// throw new \Exception('error');
$start = now();
sleep(1);
return 'sleep_1 start at ' . $start . ' / sleep_1_done at ' . now();
});
// 路由2,暫停2秒
Route::get('sleep_2', function () {
$start = now();
sleep(2);
return 'sleep_2 start at ' . $start . ' / sleep_2_done at ' . now();
});
// 路由3,暫停3秒
Route::get('sleep_3', function () {
$start = now();
sleep(3);
return 'sleep_3 start at ' . $start . ' / sleep_3_done at ' . now();
});
// 路由4,暫停4秒
Route::get('sleep_4', function () {
$start = now();
sleep(4);
return 'sleep_4 start at ' . $start . ' / sleep_4_done at ' . now();
});

可以看到執行結果

整個流程只有跑 4 秒,證明四個請求是並發,I/O 非阻塞的

接下來會做一些遇到例外的展示

假設有其中一個請求失敗了

Route::get('sleep_2', function () {
// 拋列外,假設該請求失敗了
throw new
\Exception('error');
$start = now();
sleep(2);
return 'sleep_2 start at ' . $start . ' / sleep_2_done at ' . now();
});

會發現 接收端會報錯誤

這時候 接收端就需要 做些修改 改成使用

Promise\settle($promises)->wait();

來接收 responses

// Wait for the requests to complete, even if some of them fail
$responses = Promise\settle($promises)->wait();

foreach ($responses as $key => $response) {
dump($key . ' 回傳狀態: ' . $response['state']);
}

這種方式 就算有請求某個錯誤也不會導致整個並發請求中止

當請求成功時,狀態是fulfilled,失敗時,狀態是rejected

這樣就可以只接收成功的回傳訊息就可以了

// Wait for the requests to complete, even if some of them fail
$responses = Promise\settle($promises)->wait();

foreach ($responses as $key => $response) {
$state = Arr::get($response, 'state');
if ($state === 'fulfilled') {
$value = Arr::get($response, 'value');
dump($value->getBody()->getContents());
}
}

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response