From 8cbaf5423b4be22af80a6c350172bee3adc5d1a1 Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Thu, 6 Aug 2020 12:49:03 -0700 Subject: [PATCH] support doing exponential backoff in non-blocking mode in Swoole --- README.md | 2 ++ composer.json | 4 ++++ src/ExponentialBackoff.php | 39 ++++++++++++++++++++++++++++++++++---- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 18645e7..2671d87 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Exponential back-offs prevent overloading an unavailable service by doubling the timeout each iteration. This class uses an exponential back-off algorithm to calculate the timeout for the next request. +This library allows doing exponential backoff in non-blocking mode in [Swoole](https://github.com/swoole/swoole-src). + # Installation ```bash diff --git a/composer.json b/composer.json index c881298..e2e2dc7 100644 --- a/composer.json +++ b/composer.json @@ -9,8 +9,12 @@ "require-dev": { "crowdstar/reflection": "~1.0.0", "phpunit/phpunit": "~7.0", + "swoole/ide-helper": "@dev", "squizlabs/php_codesniffer": ">=2.0" }, + "suggest": { + "ext-swoole": "Allow to do exponential backoff in non-blocking mode in Swoole" + }, "autoload": { "psr-4": { "CrowdStar\\Backoff\\": "src" diff --git a/src/ExponentialBackoff.php b/src/ExponentialBackoff.php index 76a7651..d275ecc 100644 --- a/src/ExponentialBackoff.php +++ b/src/ExponentialBackoff.php @@ -21,6 +21,7 @@ namespace CrowdStar\Backoff; use Closure; +use Swoole\Coroutine; /** * Class ExponentialBackoff @@ -35,11 +36,21 @@ class ExponentialBackoff public const TYPE_MICROSECONDS = 1; public const TYPE_SECONDS = 2; + protected const SAPI_DEFAULT = 1; + protected const SAPI_SWOOLE = 2; + /** * @var int */ protected $type = self::TYPE_MICROSECONDS; + /** + * @var string + * @see \CrowdStar\Backoff\ExponentialBackoff::SAPI_DEFAULT + * @see \CrowdStar\Backoff\ExponentialBackoff::SAPI_SWOOLE + */ + protected $sapi; + /** * @var int */ @@ -55,8 +66,10 @@ class ExponentialBackoff */ protected $retryCondition; - public function __construct(AbstractRetryCondition $retryCondition) + public function __construct(AbstractRetryCondition $retryCondition, int $sapi = 0) { + $this->sapi = $sapi ?: (extension_loaded('swoole') ? self::SAPI_SWOOLE : self::SAPI_DEFAULT); + $this->setRetryCondition($retryCondition); } @@ -177,10 +190,28 @@ protected function sleep(): self { switch ($this->getType()) { case self::TYPE_MICROSECONDS: - usleep($this->getTimeoutMicroseconds($this->getCurrentAttempts())); + $microSeconds = $this->getTimeoutMicroseconds($this->getCurrentAttempts()); + switch ($this->sapi) { + case self::SAPI_SWOOLE: + // Minimum execution delay in Swoole is 1ms. + Coroutine::sleep(max($microSeconds / 1000000, 0.001)); + break; + default: + usleep($microSeconds); + break; + } break; case self::TYPE_SECONDS: - usleep($this->getTimeoutSeconds($this->getCurrentAttempts())); + $seconds = $this->getTimeoutSeconds($this->getCurrentAttempts()); + switch ($this->sapi) { + case self::SAPI_SWOOLE: + // Minimum execution delay in Swoole is 1ms. + Coroutine::sleep(max($seconds, 0.001)); + break; + default: + sleep($seconds); + break; + } break; default: throw new Exception("invalid backoff type '{$this->getType()}'"); @@ -194,7 +225,7 @@ protected function sleep(): self */ protected function getTimeoutSeconds(int $iteration, int $initialTimeout = 1): int { - return ($this->getTimeoutMicroseconds($iteration, $initialTimeout * 1000000) / 1000000); + return (int) ($this->getTimeoutMicroseconds($iteration, $initialTimeout * 1000000) / 1000000); } /**