prismamedia / php-endeavor
PHP 工具,用于重试任何操作,采用各种策略
Requires
- php: >=7.4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.5
- phpstan/phpstan: ^1.4
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-08-25 17:20:57 UTC
README
Endeavor 是一个 PHP 工具,用于重试任何操作,采用各种策略。
要求
- PHP 7.4+
安装
使用 Composer
composer require prismamedia/php-endeavor
用法
简单地将您想要重试的代码封装在 Closure
中,然后在 run()
方法中调用它
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\ConstantStrategy; $endeavor = new Endeavor(new ConstantStrategy(500)); $endeavor->run(function () { // Code which can throw any \Throwable });
默认情况下,Endeavor 将尝试运行给定的代码 5 次。如果第一次尝试成功,Endeavor 将停止。但如果代码每次尝试都失败,Endeavor 将在最后一次尝试时抛出异常。
可以使用构造函数的第二个参数指定最大尝试次数
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\ConstantStrategy; $endeavor = new Endeavor(new ConstantStrategy(500), 3); $endeavor->run(function () { // Code which can throw any \Throwable });
在这个例子中,它将运行代码 3 次,如果在第三次尝试时仍然失败,则抛出异常。
请注意,最大尝试次数包括第一次。
可以使用构造函数的第三个参数指定最大延迟,以创建一个上限
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\ExponentialStrategy; $endeavor = new Endeavor(new ExponentialStrategy(1000), 5, 5000); $endeavor->run(function () { // Code which can throw any \Throwable });
在这个例子中,代码将以指数策略执行 5 次,每次尝试的延迟翻倍,最大延迟为 5 秒。
策略
根据预期的重试间隔,Endeavor 可以使用各种策略进行实例化。
每个策略都需要一个以毫秒为单位的 delay
,并根据策略的性质计算下一次尝试的延迟。
ConstantStrategy
这是最简单的策略。它接受一个固定的延迟,并将尝试之间的间隔设置为该数值。
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\ConstantStrategy; $endeavor = new Endeavor(new ConstantStrategy(100)); $endeavor->run(function () { throw new \RuntimeException('Failing'); }); // 1st attempt: immediate // 2nd attempt: 100ms // 3rd attempt: 100ms // 4th attempt: 100ms // 5th attempt: 100ms
LinearStrategy
此策略接受一个初始延迟,并在每次尝试中将其相加。
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\LinearStrategy; $endeavor = new Endeavor(new LinearStrategy(100)); $endeavor->run(function () { throw new \RuntimeException('Failing'); }); // 1st attempt: immediate // 2nd attempt: 100ms // 3rd attempt: 200ms // 4th attempt: 300ms // 5th attempt: 400ms
ExponentialStrategy
此策略接受一个初始延迟,并在每次尝试中将它翻倍。
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\ExponentialStrategy; $endeavor = new Endeavor(new ExponentialStrategy(100)); $endeavor->run(function () { throw new \RuntimeException('Failing'); }); // 1st attempt: immediate // 2nd attempt: 100ms // 3rd attempt: 200ms // 4th attempt: 400ms // 5th attempt: 800ms
MultiplicativeStrategy
此策略接受一个初始延迟和一个乘数,然后在每次尝试中将延迟乘以该乘数。
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\MultiplicativeStrategy; $endeavor = new Endeavor(new MultiplicativeStrategy(100, 3)); $endeavor->run(function () { throw new \RuntimeException('Failing'); }); // 1st attempt: immediate // 2nd attempt: 100ms // 3rd attempt: 300ms // 4th attempt: 900ms // 5th attempt: 2700ms
错误处理
默认情况下,Endeavor 在抛出异常时会简单地重试代码,然后在达到最大尝试次数时抛出最后一次遇到的异常。
可以使用一个 Closure
指定错误处理程序,该程序将在每次不成功的尝试后执行。
它可以用于记录目的
use PrismaMedia\Endeavor\Endeavor; use PrismaMedia\Endeavor\Strategy\LinearStrategy; $endeavor = new Endeavor(new LinearStrategy(500)); $endeavor->setErrorHandler(function (Endeavor $endeavor, \Throwable $e, int $attempt) { // $endeavor is the current instance // $e is the thrown Exception during this attempt // $attempt is the current attempt number $this->logger->error( 'Something went wrong on the attempt #{attempt}: {error}', [ 'attempt' => $attempt, 'error' => $e->getMessage(), ] ); }); $endeavor->run(function () { throw new \RuntimeException('Failing'); });
或者抛出另一个异常并停止 Endeavor,如果错误无法恢复
$endeavor->setErrorHandler(function (Endeavor $endeavor, \Throwable $e, int $attempt) { if ($e instanceof OneSpecificException) { throw $e } });
还可以用来更改当前策略
$endeavor->setErrorHandler(function (Endeavor $endeavor, \Throwable $e, int $attempt) { if ($e instanceof UnreachableDatabaseException) { $endeavor->setStrategy(new ConstantStrategy(5000)); } });
测试
测试使用 Endeavor 的类可能会显著减慢测试的执行速度。
Symfony 环境
在 Symfony 项目中,可以使用 symfony/phpunit-bridge
包和包含的 ClockMock
来解决这个问题。
请参阅 文档 了解如何设置桥接和使用 @group time-sensitive
注解。
最后,在 tests/bootstrap.php
(文档),注册 Endeavor
类
# tests/boostrap.php <?php use PrismaMedia\Endeavor\Endeavor; use Symfony\Bridge\PhpUnit\ClockMock; use Symfony\Component\Dotenv\Dotenv; require dirname(__DIR__).'/vendor/autoload.php'; if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) { require dirname(__DIR__).'/config/bootstrap.php'; } elseif (method_exists(Dotenv::class, 'bootEnv')) { (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); } // Register Endeavor in ClockMock to skip the waiting time between retries ClockMock::register(Endeavor::class);
贡献
欢迎提交拉取请求。对于重大更改,请首先打开一个问题来讨论您想要更改的内容。
请确保适当地更新测试。