提及 / 重试
PHP 的重试库
Requires
- php: >=8.1
- cuyz/valinor: ^1.1
- lstrojny/functional-php: ^1.0
- mention/kebab: ^1.4.2
- react/event-loop: ^1.2
- react/promise: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.6
- mockery/mockery: ^1.6
- phpstan/extension-installer: ^1.3
- phpstan/phpstan: ^1.4.6
- phpstan/phpstan-beberlei-assert: ^1.0
- phpstan/phpstan-mockery: ^1.1
- phpstan/phpstan-strict-rules: ^1.0
- phpunit/phpunit: ^9.0
- react/async: ^4.0
- squizlabs/php_codesniffer: ^3.4
README
Retry 是一个支持自定义退避、抖动,并支持同步和异步 API 的 PHP 库。它还允许离线重试和序列化。
特性
- 支持同步和异步操作
- 可配置的延迟:常量或指数
- 抖动以防止重试尝试同步
- 在不可恢复的错误上停止重试
- 离线重试功能
- 显式序列化支持
安装
composer require mention/retry
同步 API
use Mention\Retry\Retry; use GuzzleHttp\Client; $result = Retry::sync()->retry($operation);
$operation
应该是一个可调用的函数,它将反复调用,直到以下条件之一成立:
- 函数执行成功(无异常返回),
- 抛出
PermanentError
,表示重试将产生相同的结果, - RetryStrategy 在一定的重试次数或经过一定时间后停止重试过程
成功时,Retry::sync()->retry($operation)
返回 $operation()
的值。
以下示例将重试,直到 $client->get('http://example.com/')
成功,或者 1 分钟已过(根据默认 RetryStrategy)
use Mention\Retry\Retry; use GuzzleHttp\Client; $response = Retry::sync()->retry(function() { $client = new Guzzlehttp\Client(); return $client->get('http://example.com/'); });
异步 API
异步 API 的工作方式类似,但期望操作返回一个 React\PromiseInterface
use Mention\Retry\Retry; Retry::async($loop)->retry(function() { $browser = new React\Http\Browser(); return $browser->get('http://example.com/'); });
永久错误
有时可以确定重试将不会成功。例如,在收到 HTTP 4xx 错误后重试不太可能改变结果。
在这些情况下,操作可以通过抛出 Mention\Retry\PermanentError
来停止重试过程。当抛出此异常时,retry()
停止重试并重新抛出任何内部异常或永久错误本身。
示例
use Mention\Retry\Retry; use Mention\Retry\PermanentError; use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; $response = Retry::sync()->retry(function() { $client = new Guzzlehttp\Client(); try { return $client->get('http://example.com/404'); } catch (ClientException $e) { throw PermanentError::withException($e); } });
RetryStrategy
both Retry::sync()
和 Retry::async()
都接受一个 RetryStrategy
参数,可以用来配置重试行为。
use Mention\Retry\RetryStrategy\RetryStrategyBuilder; use Mention\Kebab\Duration\Duration; $strategy = RetryStrategyBuilder::exponentialInteractive()->withMaxElapsedTime(Duration::seconds(15)); $response = Retry::sync($strategy)->retry($operation);
默认策略
如果没有传递 RetryStrategy
,则使用默认策略。它配置如下:
- 延迟是指数的,起始值为 10 毫秒,每次尝试后增加 1.5 倍
- 在延迟上应用了 0.5 的抖动,以帮助防止操作彼此同步并发送请求高峰
- 在没有成功的情况下,经过 60 秒后停止重试
可以通过调用 Mention\Retry::setDefaultRetryStrategy()
来更改默认策略
use Mention\Retry\RetryStrategy\RetryStrategyBuilder; use Mention\Kebab\Duration\Duration; $defaultStrategy = RetryStrategyBuilder::exponentialInteractive()->withMaxElapsedTime(Duration::seconds(15)); Retry::setDefaultRetryStrategy($defaultStrategy);
在不同的上下文中设置不同的默认重试策略可能很有用。例如,在 Web 上下文中执行的代码应该比在批处理上下文中执行的代码重试时间短。更改默认重试策略可以用于此目的。
在测试中,忽略默认和自定义策略可能很有用。这可以通过调用 Mention\Retry::overrideRetryStrategy()
来完成。
use Mention\Retry\RetryStrategy\RetryStrategyBuilder; $testStrategy = RetryStrategyBuilder::noDelay(); Retry::overrideRetryStrategy($defaultStrategy);
不建议在测试之外使用 Retry::overrideRetryStrategy()
。请改用 Retry::setDefaultRetryStrategy()
,或者在调用 Retry::sync()
和 Retry::async()
时传递显式策略。
自定义策略
Mention\Retry\RetryStrategy\RetryStrategyBuilder
类允许您构建自定义策略,并自定义以下参数:
- 两次尝试之间的延迟
- 应应用多少抖动到延迟
- 最大尝试次数
- 最大经过时间
构建器提供了一些预定义的策略,可以进一步自定义。请参阅 https://github.com/mentionapp/retry/blob/main/src/RetryStrategy/RetryStrategyBuilder.php。
序列化
重试策略可以通过调用->toJsonString()
方法进行序列化,并通过调用::fromJsonString()
进行反序列化。这是为了持久化存储,以便以后使用,并且库的未来版本将支持使用以前版本序列化的策略。
离线重试
RetryStrategy
是无状态的,可以独立使用以实现离线重试。例如,它可以用于计划任务系统中,以决定是否以及何时重新安排失败的任务。
try { $task->execute(); } catch (\Exception $e) { $task->increaseFailures(); $retryStrategy = $task->getRetryStrategy(); if (!$retryStrategy->shouldRetry($task->getFailuesCount(), $task->getOriginalScheduleTime())) { throw $e; } else { $delay = $retryStrategy->getDelay($task->getFailuesCount()); $task->rescheduleWithDelay($delay); } }