2023-09-01

重写后的标题为:Laravel中的任务调度

重写后的标题为:Laravel中的任务调度

在本文中,我们将介绍 Laravel Web 框架的一项令人兴奋的功能——任务调度。我们将了解 Laravel 如何允许您管理应用程序中的计划任务。此外,我们最终还将创建自己的自定义计划任务以用于演示目的。

Laravel 框架允许您设置计划任务,这样您就不必担心在系统级别设置它们。您可以在设置计划任务时摆脱复杂的 cron 语法,因为 Laravel 允许您以用户友好的方式定义它们。

我们将首先介绍如何设置传统的 cron 作业,然后我们将探索 Laravel 实现它的方式。在本文的后半部分,我们将通过创建几个自定义计划任务来尝试一下,这些任务应该提供对该主题的实际洞察。

传统计划任务设置

在日常应用程序开发中,您经常需要定期执行某些脚本或命令。如果您使用 *nix 系统,您可能知道 cron 作业处理这些命令。另一方面,它们在基于 Windows 的系统上被称为计划任务。

让我们快速浏览一下基于 *nix 的 cron 作业的简单示例。

*/5 * * * * /web/statistics.sh
登录后复制

非常简单——它每五分钟运行一次statistics.sh文件!

虽然这是一个非常简单的示例,但您通常需要实现更复杂的用例。复杂的系统要求您定义以不同时间间隔运行的多个 cron 作业。

让我们看看复杂的 Web 应用程序必须在后端定期执行的一些任务。

  • 清理数据库后端不需要的数据。
  • 更新前端缓存索引以保持最新。
  • 计算网站统计信息。
  • 发送电子邮件。
  • 备份不同的网站元素。
  • 生成报告。
  • 还有更多。

正如您所看到的,有很多东西等待定期并且以不同的时间间隔运行。如果您是经验丰富的系统管理员,则可以轻松地为所有这些任务定义 cron 作业,但有时我们作为开发人员希望有一种更简单的方法。

幸运的是,Laravel 附带了一个内置的任务调度 API,它允许您以前所未有的方式定义计划任务。是的,下一节就是关于 Laravel 任务调度的基础知识。

Laravel 之道

在前面的部分中,我们介绍了设置 cron 作业的传统方法。在本节中,我们将在任务调度 API 的上下文中详细介绍 Laravel。

在我们继续之前,需要了解的重要一点是 Laravel 提供的调度功能就像任何其他功能一样,不会自动调用。因此,如果您认为不需要在系统级别执行任何操作,那么我想说您就不走运了。

事实上,如果您希望使用 Laravel 调度系统,您应该做的第一件事就是设置每分钟运行一次的 cron 作业,并调用以下代码片段中显示的 artisan 命令。

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
登录后复制

上面的 artisan 命令调用 Laravel 调度程序,然后执行应用程序中定义的所有待处理的 cron 作业。

当然,我们还没有看到如何在 Laravel 应用程序中定义计划任务,这就是我们接下来要深入讨论的内容。

这是 App/Console/Kernel 类的 schedule 方法,如果您希望定义特定于应用程序的计划任务,则需要使用该方法。

继续获取 app/Console/Kernel.php 文件的内容。

<?php

namespace App/Console;

use Illuminate/Console/Scheduling/Schedule;
use Illuminate/Foundation/Console/Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  /Illuminate/Console/Scheduling/Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // $schedule->command('inspire')->hourly();
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}
登录后复制

正如您所看到的,核心代码本身提供了一个有用的示例。在上面的示例中,Laravel 每小时运行 inspire artisan 命令。您不觉得语法一开始就很直观吗?

事实上,Laravel 允许您通过多种不同的方式定义计划任务:

  • 使用闭包/可调用。
  • 调用 artisan 命令。
  • 执行 shell 命令。

此外,还有大量内置的调度频率可供您选择:

  • 每分钟/每五分钟
  • 每小时/每天/每周/每季度/每年
  • 一天中的特定时间
  • 还有更多

事实上,我想说它提供了一套完整的例程,因此您无需接触 shell 即可创建自定义 cron 作业!

是的,我看得出您很想知道如何实现您的自定义计划任务,这也是我在文章开头所承诺的。

在 Laravel 中创建您的第一个计划任务

正如我们所讨论的,Laravel 允许您通过不同的方式定义计划任务。让我们逐一了解其工作原理。

闭包/可调用方法

调度 API 提供了 call 方法,该方法允许您执行可调用函数或闭包函数。让我们使用以下代码修改 app/Console/Kernel.php 文件。

<?php

namespace App/Console;

use Illuminate/Console/Scheduling/Schedule;
use Illuminate/Foundation/Console/Kernel as ConsoleKernel;
use Illuminate/Support/Facades/DB;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  /Illuminate/Console/Scheduling/Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // the call method
        $schedule->call(function () {
          $posts = DB::table('posts')
            ->select('user_id', DB::raw('count(*) as total_posts'))
            ->groupBy('user_id')
            ->get();
     
          foreach($posts as $post)
          {
            DB::table('users_statistics')
              ->where('user_id', $post->user_id)
              ->update(['total_posts' => $post->total_posts]);
          }
        })->everyThirtyMinutes();
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}
登录后复制

如您所见,我们将闭包函数作为 call 方法的第一个参数传递。另外,我们将频率设置为每 30 分钟一次,因此它将每 30 分钟执行一次关闭函数!

在我们的示例中,我们计算每个用户的帖子总数并相应地更新 statistics 表。

artisan 命令

除了闭包或可调用之外,您还可以安排一个 artisan 命令,该命令将按一定的时间间隔执行。事实上,它应该是优于闭包的首选方法,因为它同时提供了更好的代码组织和可重用性。

继续使用以下内容修改 app/Console/Kernel.php 文件的内容。

<?php

namespace App/Console;

use Illuminate/Console/Scheduling/Schedule;
use Illuminate/Foundation/Console/Kernel as ConsoleKernel;
use Illuminate/Support/Facades/DB;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        'App/Console/Commands/UserStatistics'
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  /Illuminate/Console/Scheduling/Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('statistics:user')->everyThirtyMinutes();
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}
登录后复制

这是 command 方法,如果您希望安排 artisan 命令,如上面的代码片段所示,您可以使用该方法。您需要传递 artisan 命令签名作为 command 方法的第一个参数。

当然,你还需要在app/Console/Commands/UserStatistics.php处定义相应的artisan命令。

<?php
namespace App/Console/Commands;
 
use Illuminate/Console/Command;
use Illuminate/Support/Facades/DB;
 
class UserStatistics extends Command
{
  /**
   * The name and signature of the console command.
   *
   * @var string
   */
  protected $signature = 'statistics:user';
 
  /**
   * The console command description.
   *
   * @var string
   */
  protected $description = 'Update user statistics';
 
  /**
   * Create a new command instance.
   *
   * @return void
   */
  public function __construct()
  {
    parent::__construct();
  }
 
  /**
   * Execute the console command.
   *
   * @return mixed
   */
  public function handle()
  {
    // calculate new statistics
    $posts = DB::table('posts')
      ->select('user_id', DB::raw('count(*) as total_posts'))
      ->groupBy('user_id')
      ->get();
     
    // update statistics table
    foreach($posts as $post)
    {
      DB::table('users_statistics')
      ->where('user_id', $post->user_id)
      ->update(['total_posts' => $post->total_posts]);
    }
  }
}
登录后复制

exec 命令

我们可以说到目前为止我们讨论的方法是特定于 Laravel 应用程序本身的。此外,Laravel 还允许您安排 shell 命令,以便您也可以运行外部应用程序。

让我们通过一个简单的示例来演示如何每天备份数据库。

<?php
namespace App/Console;

use Illuminate/Console/Scheduling/Schedule;
use Illuminate/Foundation/Console/Kernel as ConsoleKernel;
 
class Kernel extends ConsoleKernel
{
  /**
   * Define the application's command schedule.
   *
   * @param  /Illuminate/Console/Scheduling/Schedule  $schedule
   * @return void
   */
  protected function schedule(Schedule $schedule)
  {
    // exec method
    $host = config('database.connections.mysql.host');
    $username = config('database.connections.mysql.username');
    $password = config('database.connections.mysql.password');
    $database = config('database.connections.mysql.database');
     
    $schedule->exec("mysqldump -h {$host} -u {$username} -p{$password} {$database}")
      ->daily()
      ->sendOutputTo('/backups/daily_backup.sql');
  }
}
登录后复制

从代码中可以明显看出,您需要使用调度程序的 exec 方法,并且需要将要运行的命令作为其第一个参数传递。

除此之外,我们还使用了 sendOutputTo 方法,它允许您收集命令的输出。另一方面,有一个方法,emailOutputTo,它允许您通过电子邮件发送输出内容!

如何防止任务重叠

在本节中,我们将了解如何防止任务重叠。假设您已经定义了一个任务,并且您想要确保如果它已经在运行,Laravel 不应该运行同一任务的另一个实例。默认情况下,Laravel 将始终开始运行计划任务,即使同一任务的前一个实例已经在运行但尚未完成。

那么让我们看看如何避免重叠计划任务。

$schedule->command('statistics:user')->everyThirtyMinutes()->withoutOverlapping();
登录后复制

正如你所看到的,你只需要使用 withoutOverlapping 方法来确保 Laravel 不会与已经运行的任务重叠。默认情况下,Laravel 重叠任务之前的锁定时间为 24 小时。如果您想覆盖它,可以按照以下代码片段所示进行操作。

$schedule->command('statistics:user')->everyThirtyMinutes()->withoutOverlapping(30);
登录后复制

在上面的示例中,Laravel 等待 30 分钟才清除重叠锁。

如何定义后台任务

如果您同时安排多个任务,Laravel 会按顺序运行它们。因此,如果您有一个需要很长时间才能执行的任务,则下一个计划任务将不得不等待很长时间。为了避免这种情况,您可以在后台执行此类任务。

让我们快速看一下下面的示例,了解如何定义后台任务。

$schedule->command('statistics:user')->daily()->runInBackground();
登录后复制

如您所见,您可以使用 runInBackground 方法来定义后台任务。

结论

今天,我们了解了 Laravel Web 框架中的任务调度 API。看到它如何轻松地管理需要定期运行的任务真是令人着迷。

在文章的开头,我们讨论了传统的设置计划任务的方式,接下来我们介绍了 Laravel 的设置方式。在本文的后半部分,我们通过几个实际示例来演示任务调度概念。

我希望您喜欢这篇文章,并且您应该对在 Laravel 中设置计划任务更有信心。对于那些刚刚开始使用 Laravel 或希望通过扩展来扩展您的知识、网站或应用程序的人,我们在 Envato Market 上提供了多种您可以学习的内容。

以上就是重写后的标题为:Laravel中的任务调度的详细内容,更多请关注php中文网其它相关文章!

https://www.php.cn/faq/600622.html

发表回复

Your email address will not be published. Required fields are marked *