eventsauce / backoff
退避策略接口
1.2.0
2023-09-17 20:31 UTC
Requires
- php: ^8.1
Requires (Dev)
- phpunit/phpunit: ^10
README
此库提供封装退避策略的接口。
composer require eventsauce/backoff
利用退避策略
退避策略在尝试重试特定任务的代码块中应用。
<?php use EventSauce\BackOff\BackOffStrategy; class BusinessLogic { public function __construct( private ExternalDependency $dependency, private BackOffStrategy $backOff, ) {} public function performAction(): void { $tries = 0; start: try { ++$tries; $this->dependency->actionThatMayFail(); } catch (Throwable $throwable) { $this->backOff->backOff($tries, $throwable); goto start; } } }
指数退避
一个著名的退避策略是指数退避,这是默认提供的策略。
sleep = initial_delay * (base ^ (number_of_tries - 1)
<?php use EventSauce\BackOff\ExponentialBackOffStrategy; $backOff = new ExponentialBackOffStrategy( 100000, // initial delay in microseconds, 0.1 seconds 15, // max number of tries 2500000, // (optional) max delay in microseconds, default 2.5 seconds 2.0, // (optional) base to control the growth factor, default 2.0 ); $businessLogic = new BusinessLogic(new ExternalDependency(), $backOff); try { $businessLogic->performAction(); } catch (Throwable $throwable) { // handle the throwable }
斐波那契退避
斐波那契退避策略根据斐波那契数列增加退避时间。
sleep = initial_delay * fibonacci(number_of_tries)
<?php use EventSauce\BackOff\FibonacciBackOffStrategy; $backOff = new FibonacciBackOffStrategy( 100000, // initial delay in microseconds, 0.1 seconds 15, // max number of tries 2500000, // (optional) max delay in microseconds, default 2.5 seconds ); $businessLogic = new BusinessLogic(new ExternalDependency(), $backOff); try { $businessLogic->performAction(); } catch (Throwable $throwable) { // handle the throwable }
线性退避
线性退避策略线性增加退避时间。
sleep = initial_delay * number_of_tries
<?php use EventSauce\BackOff\LinearBackOffStrategy; $backOff = new LinearBackOffStrategy( 100000, // initial delay in microseconds, 0.1 seconds 15, // max number of tries 2500000, // (optional) max delay in microseconds, default 2.5 seconds ); $businessLogic = new BusinessLogic(new ExternalDependency(), $backOff); try { $businessLogic->performAction(); } catch (Throwable $throwable) { // handle the throwable }
抖动
当许多客户端被迫重试时,具有确定性的间隔可能会导致许多客户端同时重试。向其中添加随机性可以确保重试客户端在时间上分散。这种随机性确保客户端不太可能同时重试。
使用抖动
每个接受休眠的策略都接受一个EventSauce\BackOff\Jitter\Jitter
实现。
use EventSauce\BackOff\ExponentialBackOffStrategy; use EventSauce\BackOff\FibonacciBackOffStrategy; use EventSauce\BackOff\LinearBackOffStrategy; $exponential = new ExponentialBackOffStrategy(100000, 25, jitter: $jitter); $fibonacci = new FibonacciBackOffStrategy(100000, 25, jitter: $jitter); $linear = new LinearBackOffStrategy(100000, 25, jitter: $jitter);
全抖动
全抖动使用从0到初始计算的休眠时间之间的随机值。
sleep = number_between(0, sleep)
use EventSauce\BackOff\Jitter\FullJitter; $jitter = new FullJitter();
半抖动
全抖动使用从初始休眠时间的一半到全部初始休眠时间的随机值。
sleep = sleep / 2 + number_between(0 , sleep / 2)
use EventSauce\BackOff\Jitter\HalfJitter; $jitter = new HalfJitter();
分散抖动
分散抖动使用一个范围,在该范围内分散结果值。为了说明,这里有一些示例
jittered = sleep * range
base = sleep - jittered
sleep = base + number_between(0 , jittered * 2)
use EventSauce\BackOff\Jitter\ScatteredJitter; $jitter = new ScatteredJitter($range = 0.5);
设计理念
与其他指数退避库不同,此库不运行您想要重试的操作。这使得包的设计非常简单。它也不对周围代码施加任何限制。
您可以根据返回值进行重试
use EventSauce\BackOff\BackOffStrategy; function action(Client $client, BackOffStrategy $backOff): void { $tries = 0; start: $tries++; $response = $client->doSomething(); if ($response == SomeParticular::VALUE) { $backOff->backOff($tries, new LogicException('Exhausted back-off')); goto start; } }
您可以在特定异常类型上进行重试
use EventSauce\BackOff\BackOffStrategy; function action(Client $client, BackOffStrategy $backOff): void { $tries = 0; start: $tries++; try { $client->doSomething(); } catch (SpecificException $exception) { $backOff->backOff($tries, $exception); goto start; } }
选择权在你。享受吧!
PS:是的,有很多goto语句,请处理它 😎
但是Frank,我超级懒惰!
好吧好吧好吧,在这种情况下,请使用BackOffRunner
类来运行任何带有重试策略的callable
。
use EventSauce\BackOff\BackOffRunner; use EventSauce\BackOff\ExponentialBackOffStrategy; $strategy = new ExponentialBackOffStrategy(initialDelayMs: 100, maxTries: 5); $runner = new BackOffRunner($strategy); $runner->run(function () { // Do something that might throw an exception! });
只想在特定异常上重试?将异常类作为第二个构造函数参数传递。
use EventSauce\BackOff\BackOffRunner; use EventSauce\BackOff\ExponentialBackOffStrategy; $strategy = new ExponentialBackOffStrategy(initialDelayMs: 100, maxTries: 5); $runner = new BackOffRunner($strategy, LogicException::class); $runner->run(function () { // Only LogicException is retried, the rest is not! });