本文目录导读:

- 基础异常处理
- 使用
onFailure方法(Laravel 8+) - 使用
withoutOverlapping和onSuccess - 创建自定义异常处理器
- 使用 Laravel 内置的 ping 功能
- 综合示例:完善的异常处理
- 全局异常处理器
- 最佳实践建议
- 添加监控和警告
- 关键要点:
在Laravel中处理调度回调异常,有几种有效的方法:
基础异常处理
在调度任务中直接捕获
// app/Console/Kernel.php
use Illuminate\Support\Facades\Log;
use Throwable;
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
try {
// 你的定时任务逻辑
$this->processData();
} catch (Throwable $e) {
Log::error('定时任务执行失败', [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
]);
// 可以发送邮件或通知
// Notification::route('mail', 'admin@example.com')->notify(new TaskFailed($e));
}
})->everyMinute();
}
使用 onFailure 方法(Laravel 8+)
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
// 定时任务逻辑
})
->everyMinute()
->onFailure(function (Throwable $e) {
// 任务失败时的处理
Log::critical('调度任务失败', [
'exception' => $e->getMessage(),
'time' => now()->toDateTimeString()
]);
// 发送通知
// 报警、邮件等
});
}
使用 withoutOverlapping 和 onSuccess
$schedule->call(function () {
// 任务逻辑
})
->everyFiveMinutes()
->withoutOverlapping(10) // 防止任务重叠
->onSuccess(function () {
Log::info('任务执行成功');
})
->onFailure(function (Throwable $e) {
Log::error('任务执行失败: ' . $e->getMessage());
});
创建自定义异常处理器
创建自定义异常类
// app/Exceptions/ScheduleTaskException.php
namespace App\Exceptions;
use Exception;
class ScheduleTaskException extends Exception
{
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
创建调度任务基类
// app/Console/Commands/BaseScheduleCommand.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Throwable;
abstract class BaseScheduleCommand extends Command
{
abstract protected function executeTask();
public function handle()
{
$startTime = microtime(true);
try {
$result = $this->executeTask();
$duration = round(microtime(true) - $startTime, 2);
Log::info('调度任务成功', [
'command' => static::class,
'duration' => $duration . '秒'
]);
return $result;
} catch (Throwable $e) {
Log::error('调度任务异常', [
'command' => static::class,
'message' => $e->getMessage(),
'line' => $e->getLine(),
'file' => $e->getFile()
]);
$this->sendFailureNotification($e);
return 1; // 返回非零值表示失败
}
}
protected function sendFailureNotification(Throwable $e)
{
// 发送邮件、Slack等通知
// Notification::route('mail', config('app.admin_email'))
// ->notify(new ScheduleTaskFailed($e));
}
}
使用 Laravel 内置的 ping 功能
$schedule->call(function () {
// 任务逻辑
})
->everyMinute()
->pingBefore('https://api.example.com/job/started')
->thenPing('https://api.example.com/job/finished')
->onFailure(function (Throwable $e) {
// 发送错误通知
Log::error('任务失败', $e->getMessage());
});
综合示例:完善的异常处理
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
$job = new ProcessDataJob();
$job->handle();
})
->name('process-data')
->withoutOverlapping(30) // 防止重叠
->runInBackground() // 后台运行
->onSuccess(function () {
Log::info('数据处理成功', ['time' => now()]);
})
->onFailure(function (Throwable $e) {
Log::error('数据处理失败', [
'message' => $e->getMessage(),
'time' => now()->toDateTimeString()
]);
// 发送错误通知
Notification::route('mail', config('mail.admin_address'))
->notify(new ScheduleTaskFailed($e));
// 或者发送到钉钉/Slack群
// Http::post(config('services.dingtalk.webhook'), [
// 'msgtype' => 'text',
// 'text' => [
// 'content' => '调度任务失败: ' . $e->getMessage()
// ]
// ]);
});
// 也可以设置重试
$schedule->call(function () {
try {
// 任务逻辑
} catch (Throwable $e) {
// 如果失败,在5分钟后重试
Log::warning('任务失败,准备重试', ['error' => $e->getMessage()]);
throw $e;
}
})
->everyMinute()
->retry(3, 60); // 重试3次,间隔60秒
}
全局异常处理器
// app/Exceptions/Handler.php
public function register()
{
$this->reportable(function (Throwable $e) {
if ($e instanceof ScheduleTaskException) {
Log::channel('schedule')->error('调度任务异常', [
'exception' => $e
]);
// 发送通知
$this->notifyAdmin($e);
}
});
}
protected function notifyAdmin(Throwable $e)
{
// 发送邮件/短信/微信通知
}
最佳实践建议
// 建议将调度任务拆分为独立的Job类
$schedule->job(new ProcessDataJob('param1', 'param2'))
->everyMinute()
->onFailure(function (Throwable $e) {
// 记录失败日志
Log::error('队列任务失败', ['message' => $e->getMessage()]);
// 可以计算连续失败次数,达到阈值后发送告警
cache()->increment('schedule:fail_count', 1);
$failCount = cache()->get('schedule:fail_count', 0);
if ($failCount >= 5) {
// 发送严重告警
alertAdmin("连续5次调度失败,最后一次错误: " . $e->getMessage());
// 重置计数
cache()->forget('schedule:fail_count');
}
});
添加监控和警告
use Carbon\Carbon;
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
$start = now();
try {
// 任务逻辑
$duration = now()->diffInSeconds($start);
// 如果执行时间过长,记录警告
if ($duration > 300) {
Log::warning('任务执行时间过长', ['duration' => $duration]);
}
} catch (Throwable $e) {
Log::error('任务执行失败', [
'message' => $e->getMessage(),
'start' => $start->toDateTimeString(),
'duration' => now()->diffInSeconds($start)
]);
throw $e;
}
})
->everyMinute()
->pingBefore('https://example.com/ping')
->thenPing('https://example.com/pong');
}
关键要点:
- 错误日志:始终记录完整的异常信息,包括堆栈
- 通知机制:配置邮件、Slack等通知渠道
- 重试策略:合理设置重试次数和间隔
- 防止重叠:使用
withoutOverlapping防止任务堆积 - 监控告警:持续监控任务健康状态
- 异常分类:区分业务异常和系统异常
这样就能全面捕获和处理调度任务的异常,确保系统的稳定性和可维护性。