别让你的预发布服务器挂掉:Laravel 中的独立任务调度
Source: Dev.to
问题
如果你在构建真实的应用,就需要一个预发布(staging)环境。我在一台 Digital Ocean Droplet(2 vCPU)上运行一个 Laravel 10 应用的简单预发布环境。某天服务器卡死;htop 显示两个 CPU 核心都在 100 % 运行,内存几乎已满:
1[||||||||||||||||||100.0%]
2[||||||||||||||||||100.0%]
Mem[||||||||||||||| 3.2G/4.0G]
两个 CPU 都被占满,内存停留在 4 GB 中的 3.2 GB。
为什么重要
快速执行 ps aux 并查看日志后指向了调度器。那些在生产环境中有意义的重型计划任务——价格导出、数千商品的供应商同步、巨量数据处理——正把较小的预发布服务器压垮。预发布并不需要这些任务频繁运行,甚至根本不需要运行。
解决方案
根据环境拆分 schedule() 方法。在 Laravel 10 中可以编辑 app/Console/Kernel.php(Laravel 11+ 已将其移动到 routes/console.php)。
final class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule): void
{
$this->scheduleCommon($schedule);
if ($this->app->environment(AppEnvironmentEnum::PRODUCTION->value)) {
$this->scheduleProduction($schedule);
}
if ($this->app->environment(AppEnvironmentEnum::STAGING->value)) {
// 对预发布来说频率更低或工作负载更小
$this->scheduleStaging($schedule);
}
}
}
如何组织
公共调度
在所有环境都需要运行的任务:清理、裁剪、基础维护。
private function scheduleCommon(Schedule $schedule): void
{
// 在任何环境保持数据库精简
$schedule->command(PruneCommand::class, ['--model' => [CartItem::class]])->hourly();
$schedule->command(PruneCommand::class, ['--model' => [OrderItemNotification::class]])->daily();
// 清除过期令牌
$schedule->command(PruneExpired::class)->dailyAt('03:00');
// 基础通知
$schedule->command(TelegramNotificationSendCommand::class)->hourly()->withoutOverlapping();
$schedule->command(UserSendConfirmationNotificationCommand::class)->hourly();
}
生产调度
需要频繁执行且资源充足的重型操作。
private function scheduleProduction(Schedule $schedule): void
{
// 搜索索引同步
$schedule->command(SearchIndexSyncDiff::class)->dailyAt('05:00');
// 自动价格导出——一天多次
$schedule->command(PriceExportAutoDispatchCommand::class)
->cron('15 6,8,10,12,14,16,18 * * *');
// 供应商同步——一天 7 次
$schedule->command(TmSyncPriceExportProduct::class)
->cron('1 6,8,10,12,14,16,18 * * *');
// 商品可用性更新
$schedule->command(ProductAvailabilityUpdateCommand::class)
->hourlyAt(15)
->between('08:00', '19:00');
}
预发布调度
相同的命令,但频率更低——只保留实际测试需要的。
private function scheduleStaging(Schedule $schedule): void
{
// 价格导出——一天一次即可
$schedule->command(PriceExportAutoDispatchCommand::class)
->cron('15 6 * * *');
// 供应商同步——一天一次
$schedule->command(TmSyncPriceExportProduct::class)
->cron('1 6 * * *');
// 为调试更频繁地测试挂起订单
$schedule->command(OrderProcessSuspendedCommand::class)
->everyMinute()
->between('08:00', '19:00')
->withoutOverlapping();
}
结果
拆分工作负载后,预发布服务器的 CPU 降至 15‑20 %,内存使用稳定在约 1.5 GB。
| 环境 | CPU 使用率 | 内存使用情况 |
|---|---|---|
| 预发布(之前) | 100 % | 3.2 GB / 4 GB |
| 预发布(之后) | 15‑20 % | 1.5 GB / 4 GB |
生产环境继续以满负荷运行,未受影响。
结论
从一开始就按环境划分任务调度。让预发布只运行必要的维护例程,并对重型任务进行限流。盲目把生产调度复制到预发布会迅速导致资源耗尽。根据服务器资源调整频率,基础设施就能保持健康。