crowdstar / exponential-backoff
通过每次迭代将超时时间加倍来防止过载不可用的服务。
Requires
- php: >=7.1
Requires (Dev)
- crowdstar/reflection: ~1.0.0 || ~2.0.0
- phpunit/phpunit: ~7.0 || ~8.0 || ~9.0
- swoole/ide-helper: ~5.0
Suggests
- ext-swoole: Allow to do exponential backoff in non-blocking mode in Swoole
README
摘要
指数退避通过每次迭代将超时时间加倍来防止过载不可用的服务。此类使用指数退避算法来计算下一次请求的超时时间。
此库允许在 Swoole 中以非阻塞模式进行指数退避。
安装
composer require crowdstar/exponential-backoff:~3.0.0
示例用法
在以下代码片段中,我们假设您希望将方法 MyClass::fetchData() 的返回值存储在变量 $result 中,并且您要对它进行指数退避,因为当运行方法 MyClass::fetchData() 时可能会发生意外。
1. 当返回值为空时重试
以下代码尝试使用方法 MyClass::fetchData() 获取一些非空数据。此代码段将尝试多次(默认为4次)直到我们获取一些非空数据,或者达到最大重试次数。
<?php use CrowdStar\Backoff\EmptyValueCondition; use CrowdStar\Backoff\ExponentialBackoff; $result = (new ExponentialBackoff(new EmptyValueCondition()))->run( function () { return MyClass::fetchData(); } ); ?>
2. 当抛出特定异常时重试
以下代码尝试使用方法 MyClass::fetchData() 获取一些数据,该方法可能会抛出异常。此代码段将尝试多次(默认为4次)直到我们获取一些数据,或者达到最大重试次数。
注意:内部 PHP 错误(类 Error)不会触发指数退避。它们应该手动修复。
<?php use CrowdStar\Backoff\ExceptionBasedCondition; use CrowdStar\Backoff\ExponentialBackoff; // Allow to catch multiple types of exceptions and throwable objects. $backoff = new ExponentialBackoff(new ExceptionBasedCondition(Exception::class, Throwable::class)); try { $result = $backoff->run( function () { return MyClass::fetchData(); } ); } catch (Throwable $t) { // Handle the errors here. } ?>
当最终失败时不要抛出异常
当方法调用 MyClass::fetchData() 最终失败并捕获异常时,我们可以通过重写方法 AbstractRetryCondition::throwable() 来静默异常而无需抛出它。
<?php use CrowdStar\Backoff\AbstractRetryCondition; use CrowdStar\Backoff\ExponentialBackoff; $backoff = new ExponentialBackoff( new class extends AbstractRetryCondition { public function throwable(): bool { return false; } public function met($result, ?Exception $e): bool { return (empty($e) || (!($e instanceof Exception))); } } ); $backoff->run( function () { return MyClass::fetchData(); } ); ?>
如果需要,您可以在重写方法 AbstractRetryCondition::throwable() 时定义更复杂的逻辑。
3. 当满足自定义条件时重试
以下代码尝试使用方法 MyClass::fetchData() 获取一些非空数据。此代码段与第一个示例相同,不同之处在于这里使用了一个自定义条件类,而不是类 \CrowdStar\Backoff\EmptyValueCondition。
<?php use CrowdStar\Backoff\AbstractRetryCondition; use CrowdStar\Backoff\ExponentialBackoff; $backoff = new ExponentialBackoff( new class extends AbstractRetryCondition { public function met($result, ?Exception $e): bool { return !empty($result); } } ); $result = $backoff->run( function () { return MyClass::fetchData(); } ); ?>
4. 进行指数退避时更多选项
以下代码尝试使用方法 MyClass::fetchData() 获取一些数据。此代码段与第二个示例相同,不同之处在于这里使用了一个自定义条件类,而不是类 \CrowdStar\Backoff\ExceptionBasedCondition。
在此代码段中,我们还展示了使用此包进行指数退避时有哪些选项可用。
<?php use CrowdStar\Backoff\AbstractRetryCondition; use CrowdStar\Backoff\EmptyValueCondition; use CrowdStar\Backoff\ExceptionBasedCondition; use CrowdStar\Backoff\ExponentialBackoff; $backoff = new ExponentialBackoff(new EmptyValueCondition()); $backoff = new ExponentialBackoff(new ExceptionBasedCondition()); $backoff = new ExponentialBackoff(new ExceptionBasedCondition(Exception::class, Throwable::class)); $backoff = new ExponentialBackoff( new class extends AbstractRetryCondition { public function met($result, ?Exception $e): bool { return (empty($e) || (!($e instanceof Exception))); } } ); $backoff ->setType(ExponentialBackoff::TYPE_SECONDS) ->setType(ExponentialBackoff::TYPE_MICROSECONDS) ->setMaxAttempts(3) ->setMaxAttempts(4); $result = $backoff->run( function () { return MyClass::fetchData(); } ); ?>
5. 临时禁用指数退避
有两种方法可以暂时禁用以下代码段的指数退避:
<?php $result = MyClass::fetchData(); ?>
首先,您可以通过调用方法 \CrowdStar\Backoff\ExponentialBackoff::disable() 暂时禁用指数退避。例如
<?php use CrowdStar\Backoff\EmptyValueCondition; use CrowdStar\Backoff\ExponentialBackoff; $backoff = new ExponentialBackoff(new EmptyValueCondition()); $backoff->disable(); $result = $backoff->run(function () {return MyClass::fetchData();}); ?>
您还可以使用类 \CrowdStar\Backoff\NullCondition 临时禁用指数退避
<?php use CrowdStar\Backoff\ExponentialBackoff; use CrowdStar\Backoff\NullCondition; $result = (new ExponentialBackoff(new NullCondition())) ->setRetryCondition(new NullCondition()) // The method here is for demonstration purpose. ->run(function () {return MyClass::fetchData();}); ?>
这三个代码片段都一样,方法调用 MyClass::fetchData() 的返回值赋给变量 $result。
示例脚本
示例脚本可以在 examples/ 文件夹中找到。在 CLI 下运行它们之前,请首先运行 composer update。
composer update -n