Guzzleでリトライする方法
Published:
Guzzleにはリトライミドルウェアがあり、これを使うことにより簡単にリトライ処理が出来ます。
リトライ条件
まずはどのような条件でリトライするか決めます。
リトライミドルウェアでは、通信が終了するとDeciderの処理を行います。
引数は、リトライ回数、GuzzleHttp\Psr7\Request、GuzzleHttp\Psr7\Response、例外です。
リトライ回数は0(リトライをしていない、最初の通信)から始まります。
このDeciderが true を返した場合はリトライをする、 false を返した場合はリトライをしません。
リトライ上限や、200レスポンスの時はリトライしないなど、全て自分で定義する必要があります。
サンプルは以下のとおりです。
private function retryDecider(int $maxRetry): Closure
{
return function ($retries, $request, $response, $exception) use ($maxRetry) {
if ($retries >= $maxRetry - 1) {
return false;
}
if ($exception instanceof ConnectException) {
return true;
}
if ($response === null) {
return true;
}
if ($response->getStatusCode() === 200) {
return false;
}
return true;
};
}
リトライ時の待機時間
次にリトライ時の待ち時間をdelayクロージャーで定義します。
引数はリトライ回数です。
戻り値はリトライ時の待ち時間のミリ秒です。
サンプルは以下のとおりです。
private function retryDelay(int $delayMillisecond): Closure
{
return function ($retries) use ($delayMillisecond) {
return $delayMillisecond;
};
}
サンプルでは固定値ですが、リトライ回数が引数にあるので、リトライが増える毎に待ち時間を加算するなども可能です。
リトライミドルウェアを使う
後は以下のように、ハンドラーを設定するとリトライしてくれるようになります。
public function __construct()
{
$handlerStack = HandlerStack::create();
$handlerStack->push(Middleware::retry($this->retryDecider(3), $this->retryDelay(1000)));
$this->guzzle = new Client([
'handler' => $handlerStack,
]);
}
POSTのリトライ処理
上記のコードでは、リトライでのPOST時にBodyの中身が空になります。 これはGuzzleがデータをストリームで管理していることにより起こります。 最初のPOSTでストリームが進んでしまうので、リトライ時には位置をリセットする必要があります。
まず、以下のようなリセットハンドラーを用意し、シークが進んでいたら rewind でリセットします。
private function preserveRequestBody(): Closure
{
return function ($handler) {
return function ($request, $options) use ($handler) {
if ($request->getBody()->isSeekable()) {
$request->getBody()->rewind();
}
return $handler($request, $options);
};
};
}
次にこのハンドラーを設定します。 このハンドラーはリセットハンドラーより後に定義する必要があります。
public function __construct()
{
$handlerStack = HandlerStack::create();
$handlerStack->push(Middleware::retry($this->retryDecider(3), $this->retryDelay(1000)));
$handlerStack->push($this->preserveRequestBody();
$this->guzzle = new Client([
'handler' => $handlerStack,
]);
}
これでリトライ時でもPOSTが正しく送信されるようになります。