别让你的预发布服务器挂掉:Laravel 中的独立任务调度

发布: (2025年11月30日 GMT+8 06:39)
4 min read
原文: Dev.to

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

生产环境继续以满负荷运行,未受影响。

结论

从一开始就按环境划分任务调度。让预发布只运行必要的维护例程,并对重型任务进行限流。盲目把生产调度复制到预发布会迅速导致资源耗尽。根据服务器资源调整频率,基础设施就能保持健康。

Back to Blog

相关文章

阅读更多 »