一个用于优雅处理易变操作的 Laravel 扩展包。
Requires
- php: ^8.0
- illuminate/cache: ^8.0|^9.0|^10
- illuminate/console: ^8.0|^9.0|^10
- illuminate/support: ^8.0|^9.0|^10
Requires (Dev)
- mockery/mockery: ^1.3.3
- orchestra/testbench: ^6.0|^7.0|^8.0
- phpunit/phpunit: >=8.5.23|^9
README
Flaky for Laravel 是一个帮助您处理由于第三方不可靠而可能发生间歇性失败的操作的扩展包。
这完全是测试版!请为任何问题打开问题/PR。
安装
您可以通过 Composer 安装此扩展包。
composer require hammerstone/flaky
用法
假设您有一个失败率为 20% 的易变代码块
if (Lottery::odds(1 / 5)->choose()) { throw new Exception("Oops"); }
但是,只要它在一小时内不失败,您就无所谓它是否失败。然后您可以将该代码块包装在 Flaky 保护中。
Flaky::make('my-flaky-code') ->allowFailuresForAnHour() ->run(function() { if (Lottery::odds(1 / 5)->choose()) { throw new Exception("Oops"); } })
现在,除非操作在一小时内未成功,否则异常将被静默处理。
每个易变代码实例都需要一个唯一 ID,通过 make
方法传递。这是我们跟踪失败随时间变化的方式。您可以随意创建,它只是一个缓存键。
Flaky 使用您默认的缓存存储。将来可能需要将其可配置。
抛出异常
您有几种不同的方式来控制何时抛出异常
基于时间
如果您想在一定时间后抛出异常,您有多种方法可供选择。
allowFailuresForAMinute()
allowFailuresForMinutes($minutes)
allowFailuresForAnHour()
allowFailuresForHours($hours)
allowFailuresForADay()
allowFailuresForDays($days)
allowFailuresFor($seconds = 0, $minutes = 0, $hours = 0, $days = 0)
如果您的回调抛出异常,Flaky 将检查它是否仍在宽限期内。如果是,异常将被捕获。
如果您的回调成功,则将重置截止日期。
连续失败
如果您希望采用基于数字的方法而不是基于时间的方法,则可以使用 allowConsecutiveFailures
方法。
Flaky::make('my-flaky-code') // It can fail ten times in a row. ->allowConsecutiveFailures(10) ->run(function() { // })
现在,您的函数可以连续失败 10 次,但不会触发警报,但在第 11 次失败时将抛出异常。如果回调成功,连续失败计数器将被重置。
总失败次数
如果您想在总失败次数后抛出异常,无论成功与否,您可以使用 allowTotalFailures
方法。
Flaky::make('my-flaky-code') // It can fail ten times total. ->allowTotalFailures(10) ->run(function() { // })
在第 11 次失败时将抛出异常。只有当异常被抛出后,计数器才会重置,但不是对于成功的调用。您可以将此视为“每 11 次异常抛出,无论成功与否。”
组合
您可以根据需要以任何方式组合这三种方法。
Flaky::make('my-flaky-code') // Alert after an hour. ->allowFailuresForAnHour() // Alert after the third consecutive failure. ->allowConsecutiveFailures(3) // Alert after the tenth failure. ->allowTotalFailures(10) ->run(function() { // })
报告而不是抛出
默认情况下,Flaky 实际上会 抛出
异常,如果它超出了您定义的范围。您可以选择使用 Laravel 的 report
方法来 报告
而不是抛出该异常。
Flaky::make('my-flaky-code') ->allowFailuresForAnHour() // Don't throw, but use `report()` instead. ->reportFailures() ->run(function() { // })
这允许您仍然收到警报,但继续处理,如果您需要的话。(这对循环或长时间运行的过程很有帮助。)
重试
如果您想立即重试一些易变代码,您可以使用 retry
方法,该方法在底层使用 Laravel 的 retry
辅助程序。重试过程中的任何失败都不会计入总或连续失败次数。如果您的函数重试了最大次数并且没有成功,那么这将被视为一次失败。
Flaky::make('my-flaky-code') ->allowFailuresForAnHour() // Retry 3 times, with 500ms between. ->retry(3, 500) ->run(function() { // })
您还可以选择重试一种单个类型的异常。
Flaky::make('my-flaky-code') ->allowFailuresForAnHour() // Only retry TimeoutExceptions ->retry(3, 500, TimeoutException::class) ->run(function() { // })
或者多种类型的异常。
Flaky::make('my-flaky-code') ->allowFailuresForAnHour() // Only retry TimeoutExceptions and FooBarExceptions ->retry(3, 500, [TimeoutException::class, FooBarException::class]) ->run(function() { // })
或者通过您自己的方法。
Flaky::make('my-flaky-code') ->allowFailuresForAnHour() // Pass through your own $when callback ->retry(3, 500, function($exception) { // }) ->run(function() { // })
访问结果。
Flaky将为您的使用返回一个Result
类。
$result = Flaky::make('my-flaky-code') ->allowFailuresForAnHour() ->run(function() { return 1; }); $result->value; // 1 $result->failed; // false $result->succeeded; // true $result->exception; // null. Would be populated if an exception was thrown. $result->throw(); // Throws an exception if present. Is a noop if not.
Flaky命令
如果您有完整的Flaky命令,您可以使用FlakyCommand
类来方便地使用。
class FlakyTestCommand extends Command { protected $signature = 'flaky {--arg=} {--flag}'; public function handle() { FlakyCommand::make($this) ->allowFailuresForAnHour() ->run([$this, 'process']); } public function process() { throw new Exception('oops'); } }
现在这个命令将具有Flaky保护,但仅当由调度器调用时。如果您手动运行此命令,Flaky不会介入,您将获得所有异常,就像您之前会得到的那样。
Flaky命令ID
FlakyCommand::make($this)
调用将根据命令的签名为您设置唯一的Flaky ID。默认情况下,每个命令的每次调用都视为同一键。如果您想根据用户输入来改变它,可以使用varyOnInput
方法。
class FlakyTestCommand extends Command { protected $signature = 'flaky {--arg=} {--flag}'; public function handle() { FlakyCommand::make($this) ->allowFailuresForAnHour() // Consider the `arg` and `flag` input when creating the unique ID. ->varyOnInput() ->run([$this, 'process']); } }
如果您想根据特定的输入而不是所有输入来改变,可以传递一个键的数组。这在每个--arg
都应该有自己的flaky保护,而改变--flag
不应该创建一组独特的保护时很有用。
class FlakyTestCommand extends Command { protected $signature = 'flaky {--arg=} {--flag}'; public function handle() { FlakyCommand::make($this) ->allowFailuresForAnHour() // Consider only the `arg` input when creating the unique ID. ->varyOnInput(['arg']) ->run([$this, 'process']); } }