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

car*_*rse 9 laravel laravel-5 laravel-artisan

我设置了一个虚拟命令作业,其handle()功能如下:

public function handle()
{
    $this->line('==================');
    $this->line('Running my job at ' . Carbon::now());
    $this->line('Ending my job at ' . Carbon::now());
}
Run Code Online (Sandbox Code Playgroud)

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

现在,在我的App\Console\Kernel课堂上,我制定了以下时间表:

protected function schedule(Schedule $schedule)
{
    $schedule
        -> command('cbh:dummyCommand')
        -> everyMinute()
        -> appendOutputTo (storage_path().'/logs/laravel_output.log');
}
Run Code Online (Sandbox Code Playgroud)

现在,我从命令行运行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
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。看来我的日程正在运行。但是,如果我在同一分钟内再次运行该命令,我的日志文件现在会显示:

==================
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
Run Code Online (Sandbox Code Playgroud)

换句话说,时间表似乎比每分钟都更频繁地运行,这在我看来违反了我在时间表中定义的规则。

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

protected function schedule(Schedule $schedule)
{
    $schedule
        -> command('cbh:dummyCommand')
        -> everyFiveMinutes()
        -> appendOutputTo (storage_path().'/logs/laravel_output.log');
}
Run Code Online (Sandbox Code Playgroud)

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

没有准备好运行的预定命令。

我可以等你多久(即超过 5 分钟),但我的日志文件仍然没有输出。

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

问题

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

有人可以给我一个线索吗?

谢谢!!

car*_*rse 16

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

protected function schedule(Schedule $schedule)
{
    $schedule
        -> command('cbh:dummyCommand')
        -> everyFiveMinutes()
        -> appendOutputTo ('/my/logs/laravel_output.log');
}
Run Code Online (Sandbox Code Playgroud)

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

更好的思考方式是,这段代码将命名命令设置为“每次当前时间的分钟数为0or时都可运行5。换句话说,如果我运行命令行参数:php artisan schedule:runat 11:04,那么响应是:

protected function schedule(Schedule $schedule)
{
    $schedule
        -> command('cbh:dummyCommand')
        -> everyFiveMinutes()
        -> appendOutputTo ('/my/logs/laravel_output.log');
}
Run Code Online (Sandbox Code Playgroud)

但是如果我在11:00or处运行相同的命令11:05,那么我们得到:

# No scheduled commands are ready to run.
Run Code Online (Sandbox Code Playgroud)

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

everyFiveMinutes()根据我的任务调度程序每 2 分钟运行一次的事实,我在我的日程安排每 10 分钟在我的文件中创建一个日志时发现了上述情况。

  • *我知道回答你自己的问题并不酷*:相反,[回答你自己的问题](https://stackoverflow.com/help/self-answer)这里不仅是允许的,而且实际上是鼓励的! (7认同)
  • 那真是个贫民窟。因此,如果我的 cron 延迟运行 60 秒,而我的任务是每小时一次,我会错过整整一个小时吗? (2认同)
  • @cartbeforehorse 我现在使用 kubernetes 来运行我的 cronjobs。启动容器并开始作业可能需要一分钟的时间。这并不完全是一个缺陷。但是,是的,我的命令是幂等的,所以应该没问题,但 Laravel 可以通过简单地记录上次运行命令的时间来优雅地处理这个问题。 (2认同)

Luc*_*zzi 15

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

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

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

因此,如果您在 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,它将执行您计划在 1:00 运行的任务 N 次

这就是不需要表或文件来控制进程启动时间的神奇之处。分钟是 cron 中的最小单位,因此除非你做错了事情(例如重复行schedule:run、修改命令运行频率超过一分钟等),否则你的 Laravel 任务将在所需的时间执行一次。

请注意:检查您的时区是否正确config/app.php。我很想知道为什么这样的事情everyMinute(), everyFiveMinutes()有效,但dailyAt('1:10')没有。当然,由于 Laravel 使用 UTC,而我使用 GMT-3(服务器时钟),所以时间上有很大差异。