1、在 Laravel 8 中,才原生实现了在队列任务中的指数回退。参考:https://learnku.com/laravel/t/50086
2、参考:http://snags88.github.io/backoff-strategy-for-laravel-jobs 。Backoff Strategy for Laravel Jobs。
3、在现代应用程序中,当出现问题时,会尝试重试导致问题的代码块。例如,如果您在使用 GMail 并且失去了互联网连接,该网站将尝试重新连接到 Google 服务器。您可能会注意到,在第一次尝试时,它会立即尝试重新连接。然后它会等待几秒钟,然后重试。然后等待 10-15 秒,然后重试。然后再等待 30-40 秒,等等。您所经历的称为重新尝试失败任务的退避策略。可以想象,对于排队的作业,采用这种策略会很棒,尤其是对于处理第三方 API 的作业。如果第三方出现故障,那么它可能需要一些时间才能恢复正常,因此采用退避策略是有益的。可悲的是,Laravel 6 的作业中没有内置退避策略。然而,通过它暴露给我们的失败作业处理,我们可以轻松编写自己的可重试作业。我们将实施的特定退避策略称为指数退避策略,因为我们以指数方式增加退避(即 2、4、8、16、32 等)。
4、现有一个 Job,可尝试的最大次数为 3 次。决定在此基础上实现指数回退。现有代码实现
class ThemeAssetUploadJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ThemeAssetGlobPatternTrait;
/**
* 任务可以尝试的最大次数。
*
* @var int
*/
public $tries = 3;
/**
* 任务失败的处理过程
*
* @param Exception $exception
* @return void
*/
public function failed(Exception $exception)
{
$themeManager = new ThemeManager();
$themeManager->themeInstallationJobFailed($exception, $this->themeInstallationTask, self::$absolutePath, self::$destination);
}
}
5、新建 RetriableJob.php
<?php
namespace Modules\ThemeStoreDb\Jobs;
use Exception;
use Illuminate\Foundation\Bus\PendingDispatch;
class RetriableJob
{
public $tries = 1; // 覆盖默认值并确保我们不会自动重新排队
public $currentRetryCount = 1;
public $maxRetries = 3; // 3, 9, 27 seconds
public $backoffFactor = 3;
/**
* 任务失败的处理过程
*
* @param Exception $exception
* @return PendingDispatch|void
*/
public function failed(Exception $exception)
{
if ($this->currentRetryCount <= $this->maxRetries) {
$this->delay(now()->addSeconds($this->backoffFactor ** $this->currentRetryCount));
$this->currentRetryCount += 1;
return dispatch($this);
}
}
}
6、编辑 ThemeAssetUploadJob.php,继承至 RetriableJob,在 handle() 方法添加日志,以记录最终的执行次数
class ThemeAssetUploadJob extends RetriableJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ThemeAssetGlobPatternTrait;
/**
* 任务可以尝试的最大次数。
*
* @var int
*/
public $tries = 1;
/**
* Execute the job.
*
* @return void
* @throws OverflowException
*/
public function handle()
{
Log::info(date("Y-m-d H:i:s"));
aa();
}
/**
* 任务失败的处理过程
*
* @param Exception $exception
* @return void
*/
public function failed(Exception $exception)
{
parent::failed($exception);
$themeManager = new ThemeManager();
$themeManager->themeInstallationJobFailed($exception, $this->themeInstallationTask, self::$absolutePath, self::$destination);
}
}
7、查看队列任务执行情况。ThemeAssetUploadJob 在第 1 次失败后,总计又重试了 3 次。间隔时间分别为:3、9、27。符合预期。如图1

PS E:\wwwroot\object> php artisan queue:work
[2023-04-07 11:26:38][9Eq40nHhxmivDgMMdlFWue9CPLVtgXlB] Processing: Modules\ThemeStoreDb\Jobs\ThemeInstallationJob
[2023-04-07 11:27:20][9Eq40nHhxmivDgMMdlFWue9CPLVtgXlB] Processed: Modules\ThemeStoreDb\Jobs\ThemeInstallationJob
[2023-04-07 11:27:20][QxjbrKqslQ31DHW4feCXGmFGwg2LNU1t] Processing: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:27:22][QxjbrKqslQ31DHW4feCXGmFGwg2LNU1t] Failed: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:27:25][lbE1431gMXnH5W5gXXmfRbI3fnlumTFt] Processing: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:27:25][lbE1431gMXnH5W5gXXmfRbI3fnlumTFt] Failed: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:27:34][WCq0aET7JOPjiHGxtbJEEt5p4BGrEpko] Processing: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:27:34][WCq0aET7JOPjiHGxtbJEEt5p4BGrEpko] Failed: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:28:01][NhYilL7Uus8oBRNgsxFzrB0x1ZKkl40o] Processing: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
[2023-04-07 11:28:01][NhYilL7Uus8oBRNgsxFzrB0x1ZKkl40o] Failed: Modules\ThemeStoreDb\Jobs\ThemeAssetUploadJob
8、再次查看生成的日志记录,以再次确认。总计执行了 4 次。间隔时间分别为:5、9、27。符合预期。如图2

[2023-04-07 11:27:20] local.INFO: 2023-04-07 11:27:20
[2023-04-07 11:27:25] local.INFO: 2023-04-07 11:27:25
[2023-04-07 11:27:34] local.INFO: 2023-04-07 11:27:34
[2023-04-07 11:28:01] local.INFO: 2023-04-07 11:28:01
9、但是,预期是当第 4 次失败后(即真正失败时),才执行 $themeManager->themeInstallationJobFailed() 方法。但是,发现在每次失败后,皆执行了 $themeManager->themeInstallationJobFailed() 方法。编辑 RetriableJob.php 。如图3

<?php
namespace Modules\ThemeStoreDb\Jobs;
use Exception;
use Illuminate\Foundation\Bus\PendingDispatch;
class Retriable
{
public $tries = 1; // 覆盖默认值并确保我们不会自动重新排队
public $currentRetryCount = 1;
public $maxRetries = 3; // 3, 9, 27 seconds
public $backoffFactor = 3;
/**
* 任务失败的处理过程
*
* @param Exception $exception
* @return PendingDispatch|false
*/
public function failed(Exception $exception)
{
if ($this->currentRetryCount <= $this->maxRetries) {
$this->delay(now()->addSeconds($this->backoffFactor ** $this->currentRetryCount));
$this->currentRetryCount += 1;
return dispatch($this);
}
return false;
}
}
10、编辑 ThemeAssetUploadJob.php,继承至 RetriableJob。
/**
* 任务失败的处理过程
*
* @param Exception $exception
* @return void
*/
public function failed(Exception $exception)
{
if (!parent::failed($exception)) {
$themeManager = new ThemeManager();
$themeManager->themeInstallationJobFailed($exception, $this->themeInstallationTask);
}
}
11、仅当第 4 次失败后(即真正失败时),才执行 $themeManager->themeInstallationJobFailed() 方法。符合预期。如图4

PHP / Laravel / Yii2 老项目维护与长期技术支持
如果你的 PHP / Laravel / Yii2 项目已经上线,但遇到原开发离职、Bug 长期无人修复、接口不稳定、性能下降、代码难以接手等问题,可以联系我做一次远程技术排查。
适合以下情况:
✅ 老旧 PHP 系统无人维护
✅ Laravel / Yii2 项目 Bug 修复
✅ 后台管理系统小功能迭代
✅ RESTful API 接口排查
✅ MySQL / Redis / Nginx 性能问题
✅ 长期远程兼职维护
可先从一次小问题开始:
✅ 线上报错排查
✅ 接口异常分析
✅ 慢查询与性能瓶颈定位
✅ 代码结构初步评估
✅ 部署环境与日志检查
如需咨询,请联系我,并注明:PHP 维护咨询。
联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com

发表回复