Laravel Artisan:`schedule:run` 是如何工作的?

Posted

技术标签:

【中文标题】Laravel Artisan:`schedule:run` 是如何工作的?【英文标题】:Laravel Artisan: How does `schedule:run` work? 【发布时间】:2018-07-18 22:58:15 【问题描述】:

我设置了一个虚拟的Command作业,其handle()函数如下:

public function handle()

    $this->line('==================');
    $this->line('Running my job at ' . Carbon::now());
    $this->line('Ending my job at ' . Carbon::now());

如您所见,它实际上并没有做任何事情,只是将几行信息返回到标准输出。

现在,在我的App\Console\Kernel 班级中,我设置了以下时间表:

protected function schedule(Schedule $schedule)

    $schedule
        -> command('cbh:dummyCommand')
        -> everyMinute()
        -> appendOutputTo (storage_path().'/logs/laravel_output.log');

现在,我从命令行运行php artisan schedule:run。我的laravel_output.log 文件中的输出为

==================
Running my job at 2018-02-08 11:01:33
Ending my job at 2018-02-08 11:01:33

到目前为止一切顺利。看来我的日程安排正在运行。但是,如果我在同一分钟内再次运行该命令,我的日志文件现在显示为:

==================
Running my job at 2018-02-08 11:01:33
Ending my job at 2018-02-08 11:01:33
==================
Running my job at 2018-02-08 11:01:51
Ending my job at 2018-02-08 11:01:51

换句话说,日程安排似乎比每分钟都运行得更频繁,在我看来,这违反了我在日程安排中定义的规则。

更令人困惑的是,我可以将计划更改为每 5 分钟而不是每分钟运行一次:

protected function schedule(Schedule $schedule)

    $schedule
        -> command('cbh:dummyCommand')
        -> everyFiveMinutes()
        -> appendOutputTo (storage_path().'/logs/laravel_output.log');

然后运行php artisan schedule:run,然后我得到以下输出

没有计划的命令准备好运行。

我可以等待任意时间(即超过 5 分钟),但我的日志文件仍然没有输出。

当我使用 Windows 任务计划程序安排我的命令时,我观察到完全相同的行为(是的,我的开发环境是一个 Windows 7 机器,是的,这是一个相当于 Windows 的 cron 作业)。

问题

那么发生了什么? artisan schedule:run 命令如何确定哪些命令正在“等待”按计划执行?我曾想象会有某种日志文件来记录以下事实:“命令 X 的时间表为 1 小时,最后一次运行是在 09:00,所以不要在 10:00 之前再次执行它",但我一直找不到这样的日志。

谁能给我一个线索?

谢谢!!

【问题讨论】:

schedule:run 需要每分钟执行一次。 @tkausl 你能详细说明一下吗?孤立地看你的评论不是很有用。 【参考方案1】:

我知道,回答你自己的问题并不酷。无论如何,让我们想象一下这是我的日程安排:

protected function schedule(Schedule $schedule)

    $schedule
        -> command('cbh:dummyCommand')
        -> everyFiveMinutes()
        -> appendOutputTo ('/my/logs/laravel_output.log');

我发现此代码并未将您的作业设置为每 5 分钟运行一次。如果命令在不到 5 分钟前运行,它也不会阻止命令再次运行。

更好的考虑方法是,这段代码将命名命令 " 设置为每次当前时间的分钟数为 05"时可运行。换句话说,如果我在11:04 处运行命令行参数:php artisan schedule:run,那么响应是:

# No scheduled commands are ready to run.

但如果我在11:0011:05 运行相同的命令,那么我们会得到:

# Running scheduled command: php artisan cbh:dummyCommand >> /my/logs/laravel_output.log 2>&1

我最终在我的日志文件中得到了输出。

当我的 everyFiveMinutes() 计划基于我的任务计划程序每 2 分钟运行一次的事实每 10 分钟在我的文件中创建一个日志时,我发现了上述情况。

【讨论】:

回答你自己的问题不太酷,我知道:相反,answering your own question 这里不仅允许,实际上是鼓励! 那是个漂亮的贫民区。因此,如果我的 cron 延迟运行 60 秒,而我的任务是每小时一次,我会错过整整一个小时吗? @mpen 是的,或多或少。但是,如果您的 Cron 作业“错过”了它的执行槽,那么您的操作系统可能存在严重问题。一种可能的解决方案是每分钟执行所有作业,但在作业本身中建立安全检查。您的作业会在执行后标记数据库或文件,然后在下一次运行时检查时间戳是否不少于一小时前。 @cartbeforehorse 我现在正在使用 kubernetes 运行我的 cronjobs。启动容器并开始工作可能需要一分钟。这不完全是缺陷。但是,是的,我的命令是幂等的,所以应该没问题,但是 Laravel 可以通过简单地记录上次运行命令的时间来优雅地处理这个问题。 哦,这不直观!感谢您分享您的发现!我知道不鼓励在 cmets 中感谢,但我感到特别感激,因为这样的事情为我省去了很多麻烦!【参考方案2】:

我回答这个只是为了让其他人知道(因为我也有同样的困惑)。

Laravel 调度程序与 Linux cron 执行完全相同相同的工作,方法是检查任务的 cronned 时间(以分钟为单位)是否与当前时间完全相同。

当您在 crontab 中设置 * * * * * ... php artisan schedule:run >> ... 时,您每分钟 0 秒运行 schedule:run,例如“1:00:00”、“1:01:00”、“1:02:00”等。

因此,如果您在 Laravel 调度程序中将命令设置为在“星期一 1:00”运行(比方说),并且您在星期一的 1:00,它将被执行,不管当前秒数。最后一部分()对于理解它的工作原理很重要。

例如,您在星期一 1:00:05(1:00 后 5 秒),因此 cron 已经启动 schedule:run 并且您的任务正在执行中。然后打开终端,转到项目的根目录并手动启动php artisan schedule:run。那时,可能是 1:00:30(1:00 后 30 秒)。好吧,现在您的任务将再次执行,因为 1:00:30 仍然是 1:00 的一部分。所以您可以在 1:00 执行 N 次 schedule:run,它会执行 N 次您计划在 1:00 运行的任务

这就是不需要表格或文件来控制进程启动时间的神奇之处。分钟是 cron 中的最小单位,所以除非你做错了事情(比如复制schedule:run 行,运行命令more often than a minute 的破解等),你的 Laravel 任务将在所需时间执行一次。

请注意:检查config/app.php 中的时区是否正确。我很想明白为什么像everyMinute(), everyFiveMinutes() 这样的东西可以正常工作,而dailyAt('1:10') 却不行。当然,由于 Laravel 在 UTC 而我在 GMT-3(服务器时钟),我的小时数有很大差异。

【讨论】:

以上是关于Laravel Artisan:`schedule:run` 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

laravel总结——任务调度

laravel总结——任务调度

laravel总结——任务调度

Laravel 5 - 如何从 Artisan 命令运行控制器方法?

windows 怎样设置laravel 任务调度

执行 artisan schedule:run 时强制所有任务运行