使用 Guzzle 中间件进行优雅的请求重试

本文介绍了如何在 Laravel 中利用 Guzzle HTTP 客户端,通过自定义中间件实现请求的优雅重试。代码示例展示了如何配置最大重试次数和间隔时间,并提供了重试决策和延迟计算的逻辑。在遇到连接错误或服务器500状态码时,请求将自动重试,提高了系统的容错性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

借鉴:

使用 Guzzle 中间件进行优雅的请求重试 | Laravel China 社区

基础配置(我图方便,直接放在了App\Common里面):

<?php
/**
 * GuzzleRetry,针对Guzzle请求重试
 */
namespace App\common;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;

class GuzzleRetry
{
    /**
     * 最大重试次数
     */
    const MAX_RETRIES = 5;
    /**
     * 每次请求间隔时间:秒
     */
    const RETRY_DELAY = 2;

    /**
     * @var Client
     */
    public $client;

    /**
     * GuzzleRetry constructor.
     */
    public function __construct($url)
    {
        // 创建 Handler
        $handlerStack = HandlerStack::create(new CurlHandler());
        // 创建重试中间件,指定决策者为 $this->retryDecider(),指定重试延迟为 $this->retryDelay()
        $handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
        // 指定 handler
        $this->client = new Client(['handler' => $handlerStack,'base_uri'=>$url]);
    }

    /**
     * retryDecider
     * 返回一个匿名函数, 匿名函数若返回false 表示不重试,反之则表示继续重试
     * @return \Closure
     */
    protected function retryDecider()
    {
        return function (
            $retries,
            Request $request,
            Response $response = null,
            RequestException $exception = null
        ) {
            // 超过最大重试次数,不再重试
            if ($retries >= self::MAX_RETRIES) {
                return false;
            }

            // 请求失败,继续重试
            if ($exception instanceof ConnectException) {
                return true;
            }

            if ($response) {
                // 如果请求有响应,但是状态码大于等于500,继续重试(这里根据自己的业务而定)
                if ($response->getStatusCode() >= 500) {
                    return true;
                }
            }

            return false;
        };
    }

    /**
     * 返回一个匿名函数,该匿名函数返回下次重试的时间(毫秒)
     * @return \Closure
     */
    protected function retryDelay()
    {
        return function ($numberOfRetries) {
            return 1000 * self::RETRY_DELAY;
        };
    }
}

具体实现:

private function request(string $method = 'POST', string $uri = '/login', array $body = [])
    {
        $headers = [
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $this->token,
            'Content-Type' => 'application/json',
            'Accept-Language' => 'en-US'
        ];
        $client = new GuzzleRetry($this->url);
        try {
            $response = $client->client->request($method, $uri, ['json' => $body, 'headers' => $headers, 'verify' => false]);
        } catch (\Exception $e) {
            Log::error('Request ' . $uri . ' failed.Error :' . $e->getMessage());
            return ["status" => false, "error" => 'Request api failed.'];
        }
        $code = $response->getStatusCode();
        if ($code > 299 || $code < 200) {
            Log::error('Request ' . $uri . 'failed.HttpCode error.');
            return ["status" => false, "error" => 'Request api failed.'];
        }
        try {
            $content = $response->getBody()->getContents();
            $content = json_decode($content, true);
            $content['status'] = (strtolower($content['status']) == 'success') ? true : false;
        } catch (\Exception $e) {
            Log::error('Request ' . $uri . 'failed.Error:Return data format error.');
            return ["status" => false, "error" => 'Request api failed.'];
        }
        return $content;
    }

需要初始化的是:

        url, 比如说是 http://127.0.0.1/api/v1

        token: XXXXX

这样就会自动重试

当然, 万一我不想那么优雅, 那就直接在发送请求那里重试吧

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL            => $url,
    CURLOPT_HTTPHEADER     => $headers,
    CURLOPT_CUSTOMREQUEST  => 'POST',
    CURLOPT_POSTFIELDS     => $request,
    CURLOPT_RETURNTRANSFER => true,//获取的信息以文件流的形式返回,不直接输出
    CURLOPT_HEADER         => false,//不返回header部分
    CURLOPT_ENCODING       => '',//Accept-Encoding编码,支持"identity"/"deflate"/"gzip",空支持所有编码
    CURLOPT_MAXREDIRS      => 10,//指定最多的HTTP重定向的数量
    CURLOPT_TIMEOUT        => 0,//连接后等待时间,0不等待。如下载mp3
    CURLOPT_FOLLOWLOCATION => true,//跟踪爬取重定向页面
    CURLOPT_HTTP_VERSION   => CURL_HTTP_VERSION_1_1,//设置curl使用的HTTP协议
    CURLOPT_SSL_VERIFYPEER => false,//禁止 cURL 验证对等证书
    CURLOPT_SSL_VERIFYHOST => false//是否检测服务器的域名与证书上的是否一致
]);
$response = curl_exec($ch);
//如果出错了, 3次重试, 每次间隔2S
$tried = 3;
while (
    isset($response['errors']) &&
    $tried > 0
){
    sleep(2);
    $response = curl_exec($ch);
    $response = json_decode($response, true);
    $tried--;
}
curl_close($ch);
return $response;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值