本文目录导读:

在Laravel中实现任务调度的失败重试机制,有以下几种方案:
使用队列的重试机制
基础配置
// config/queue.php
'connections' => [
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90, // 任务失败后重试的延迟时间(秒)
],
],
定义可重试任务
// app/Jobs/ProcessReport.php
class ProcessReport implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3; // 最大重试次数
public $backoff = [60, 300, 600]; // 每次重试的延迟时间(秒)
public $timeout = 120; // 任务超时时间
public $maxExceptions = 3; // 最大异常次数
public function handle()
{
// 业务逻辑
try {
// 处理任务
} catch (\Exception $e) {
// 记录日志
Log::error('任务失败', [
'error' => $e->getMessage(),
'attempt' => $this->attempts()
]);
throw $e;
}
}
// 失败后的回调
public function failed(\Throwable $exception)
{
Log::error('任务最终失败', [
'message' => $exception->getMessage(),
'job_id' => $this->job->getJobId()
]);
}
}
调度任务中的重试
使用调度器配置
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('report:generate')
->daily()
->withoutOverlapping() // 防止任务重叠
->onOneServer() // 只在单服务器执行
->runInBackground() // 后台运行
->onFailure(function () {
// 失败时发送通知
Notification::send($admins, new TaskFailedNotification());
});
}
手动重试机制
// app/Console/Commands/GenerateReport.php
class GenerateReport extends Command
{
protected $signature = 'report:generate';
protected $maxRetries = 3;
protected $retryDelay = 60; // 秒
public function handle()
{
$attempt = 1;
while ($attempt <= $this->maxRetries) {
try {
$this->processData();
$this->info('报表生成成功');
return 0;
} catch (\Exception $e) {
Log::error("第{$attempt}次尝试失败", [
'error' => $e->getMessage()
]);
if ($attempt < $this->maxRetries) {
$this->info("等待 {$this->retryDelay} 秒后重试...");
sleep($this->retryDelay);
$attempt++;
} else {
$this->error('所有重试都失败了');
return 1;
}
}
}
}
}
使用Redis锁实现分布式重试
// app/Console/Commands/ProcessData.php
class ProcessData extends Command
{
protected $signature = 'data:process';
public function handle()
{
$lock = Cache::lock('data-processing', 120);
try {
if ($lock->get()) {
// 检查是否有限制
$retryCount = Cache::get('data-process-retry', 0);
if ($retryCount >= 3) {
$this->error('超过最大重试次数');
return 1;
}
$this->processData();
// 成功,清除重试计数
Cache::forget('data-process-retry');
return 0;
}
} catch (\Exception $e) {
// 失败,递增重试计数
$retryCount = Cache::increment('data-process-retry');
Cache::put('data-process-retry-time', now(), 3600);
Log::error("处理失败,第{$retryCount}次重试");
// 延迟重试
$this->scheduleRetry($retryCount);
} finally {
$lock->release();
}
}
private function scheduleRetry($retryCount)
{
$delay = [5, 15, 30][$retryCount - 1] ?? 60;
// 通过队列延迟执行
ProcessDataJob::dispatch()->delay(now()->addSeconds($delay));
// 或者通过调度器
$this->line("将在 {$delay} 秒后重试");
Event::dispatch('data.process.retry', new DataProcessRetryEvent($retryCount));
}
}
使用第三方包
supervisord 配置
[program:laravel-scheduler] process_name=%(program_name)s_%(process_num)02d command=php /path/to/project/artisan schedule:work autostart=true autorestart=true user=www-data numprocs=1 redirect_stderr=true stdout_logfile=/path/to/project/storage/logs/scheduler.log stopwaitsecs=3600
Laravel Horizon
// config/horizon.php
'defaults' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'processes' => 1,
'tries' => 3,
'timeout' => 60,
'retry_after' => 90,
],
],
日志和监控
// app/Http/Middleware/TaskRetryMiddleware.php
class TaskRetryMiddleware
{
public function handle($job, $next)
{
$attempt = $job->attempts();
try {
$response = $next($job);
// 记录成功日志
Log::info('任务执行成功', [
'job' => get_class($job),
'attempts' => $attempt
]);
return $response;
} catch (\Exception $e) {
// 记录失败日志
Log::error('任务执行失败', [
'job' => get_class($job),
'attempts' => $attempt,
'error' => $e->getMessage()
]);
// 根据失败类型决定是否重试
if ($this->shouldRetry($e)) {
$job->release(60); // 延迟60秒重试
} else {
throw $e;
}
}
}
private function shouldRetry($exception)
{
// 不重试某些特定异常
return !($exception instanceof \DomainException);
}
}
最佳实践
// app/Console/Commands/ScheduleTask.php
class ScheduleTask extends Command
{
protected $signature = 'scheduler:retry-task';
public function handle()
{
$maxRetries = 3;
$retryCount = 0;
do {
try {
$this->performTask();
$this->info('任务成功');
break;
} catch (\Exception $e) {
$retryCount++;
$this->error("第 {$retryCount} 次失败: " . $e->getMessage());
if ($retryCount >= $maxRetries) {
$this->notifyFailure($e);
return 1;
}
$this->line('等待重试...');
sleep(pow(2, $retryCount) * 10); // 指数退避
}
} while ($retryCount < $maxRetries);
return 0;
}
}
注意事项
- 幂等性:确保任务可重复执行
- 事务安全:处理数据库事务回滚
- 监控告警:重试失败必须通知相关人员
- 资源控制:防止无限重试耗尽资源
选择合适的方案取决于你的具体需求:简单的任务可用内置重试,重要任务建议用队列+失败记录机制,生产环境建议配置完善的监控和告警系统。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播