Laravel8/9/10 Docker部署下SIGTERM处理:调度器与Worker
Laravel调度器处理SIGTERM信号,避免容器关闭时启动新任务
一、Laravel队列Worker的SIGTERM处理现状
Laravel 8、9、10版本会处理队列Worker的SIGTERM信号,但根据Worker.php第185行的注释,Laravel仅针对当前Worker实例处理该信号:
// Finally, we will check to see if we have exceeded our memory limits or if // the queue should restart based on other indications. If so, we'll stop // this worker and let whatever is "monitoring" it restart the process.
如果不修改supervisor(或其他监控工具)使其识别SIGTERM并不再重启Worker,这个处理的效果很有限——因为Laravel要等Worker完成当前任务后才会停止。当然也可以在supervisor停止重启队列时,执行artisan queue:restart来优雅停止所有任务。
二、调度器的核心需求
我们需要实现:当收到SIGTERM信号(比如Docker容器关闭时),php artisan schedule:run不再启动任何新命令,避免容器在进程执行中途被终止,这在部署或缩容场景中非常关键。
三、现有方案的局限性
- 将调度命令放入队列:虽然能解决问题,但会限制调度器只能处理队列任务,灵活性不足。
- Symfony的
ConsoleEvents::SIGNAL事件监听:由于schedule:run每分钟仅执行一次,执行窗口极窄,很可能错过比如第30秒发送的SIGTERM信号,无法有效拦截新任务启动。
四、可行实现方案:PCNTL信号拦截+全局状态控制
我们可以利用PHP的PCNTL扩展,在Console Kernel中监听SIGTERM信号,通过单例类记录状态,让调度器在状态标记为禁用时直接返回,不启动新任务。
步骤1:创建全局状态单例类
// app/SignalSingleton.php namespace App; class SignalSingleton { public bool $schedulerIsEnabled = true; }
步骤2:修改Console/Kernel.php处理信号并控制调度
// app/Console/Kernel.php namespace App\Console; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Illuminate\Contracts\Foundation\Application; use Illuminate\Events\Dispatcher; use Illuminate\Support\Facades\Log; use App\SignalSingleton; class Kernel extends ConsoleKernel { public function __construct(Application $app, Dispatcher $events) { parent::__construct($app, $events); $this->app->singleton(SignalSingleton::class); // 检查PCNTL扩展是否加载,没加载则跳过信号处理 if (!extension_loaded('pcntl')) { return; } // 启用异步信号处理 pcntl_async_signals(true); // 监听SIGTERM信号,标记调度器为禁用状态 pcntl_signal(SIGTERM, function (int $signo, mixed $siginfo): void { Log::info('收到SIGTERM信号,将停止启动新的调度任务'); resolve(SignalSingleton::class)->schedulerIsEnabled = false; }); } protected function schedule(\Illuminate\Console\Scheduling\Schedule $schedule): void { // 如果调度器已被禁用,直接返回,不执行任何任务 if (!resolve(SignalSingleton::class)->schedulerIsEnabled) { return; } // 在这里编写你的正常调度任务 // 示例:$schedule->command('inspire')->hourly(); // ... } }
注意事项
- 该方案依赖PHP的
pcntl扩展,需要确保运行环境已安装并启用该扩展。 - 当容器收到SIGTERM信号后,Kernel会立即标记调度器状态为禁用,后续每次
schedule:run执行时都会直接跳过任务启动逻辑,避免新任务在容器关闭前启动。
内容的提问来源于stack exchange,提问作者user7309871
相关产品推荐
相关产品推荐

