header-bg.jpg
Laravel 5.6 中优雅地使用任务调度
发表于 2019-08-02 15:04
|
分类于 PHP
|
评论次数 0
|
阅读次数 2230

QQ图片20190802143325.png

以前我对于定时任务功能都是使用 Linux 自带的 crontab 来完成,最近在 Laravel 的项目中又遇到定时任务相关的功能了,所以研究了一下 Laravel 自带的任务调度,它也是基于 crontab 完成的。

它相比直接用 crontab 运行脚本,减少了代码对外的耦合程度,更方便让我们在框架中完成定时任务的功能,所以本文记录一下我是如何在 Laravel 5.6 中使用定时任务的。

Laravel 5.6 任务调度文档

调度方式

需要调度的任务放在 app/Console/Kernel.php 中的 schedule 方法来完成, Laravel 提供了 4 种方式来完成调度任务:

定义调度

在任务不多的情况可以使用 Closure Call 的方式来直接完成,直接将逻辑写在回调函数中,传递给 call 方法,用法如下:

<?php

namespace App\Console;

use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * 应用提供的 Artisan 命令
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     *定义应用的命令调度
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('recent_users')->delete();
        })->daily();
    }
}

Artisan 命令调度

在任务较多时,建议使用 Command 的方式,例如我的项目种有定时发布视频、删除用户浏览记录等很多定时任务,所以我就使用了 Command 的方式来完成。

定义 Artisan 命令

在使用这种方式时,首先需要定义 Artisan 命令

Artisan 命令使用文档

打开编辑器的控制台,或者 Windows 打开 CMD 命令行,进入项目的根目录并运行命令:

php artisan make:command publishVideo

如上我创建了一个名为 publishVideo 的命令,这个命令的同名 PHP 文件被放在了 app/Console/Commands 目录下,文件的内容如下:

<?php

namespace App\Console\Commands;

use App\Services\VideoService;
use Illuminate\Console\Command;

class PublishVideo extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'video:publish';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'timing publish video';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        VideoService::timingPublish();
    }
}

可以看到,这个 PublishVideo 类中有 $signature$description 两个属性,以及 handle 方法。

顾名思义,$signature 属性就是命令的唯一签名、标识,我为定义为 video:publish,可以使用 php artisan list 查看当前 Laraval 项目的所有的命令标识

$description 即命令的描述,这个可以随便定义。

最后是 handle 方法,我们把需要运行的逻辑代码放在此方法即可,如果逻辑较多,可以将逻辑放在 Service 类中,减少可以看到我在这个方法中调用了 VideoService 服务的 timingPublish 方法。

调度 Artisan 命令

Artisan 命令创建完成后,接着就可以和 Closure Call 的方式一样调度 Artisan 命令了,在 app/Console/Kernel.php 中的 schedule 方法中:

protected function schedule(Schedule $schedule)
{
    $schedule->command('video:publish')->withoutOverlapping();
    $schedule->command('video:updateSrc')->withoutOverlapping();
    $schedule->command('video:storeViews')->daily();
    $schedule->command('user:destroyBrowses')->dailyAt('04:00');
}

如上,我分别调度了 4 个 Artisan 命令,执行频率分别是:每分钟、每分钟、每天凌晨 00:00、每天凌晨 04:00

调度频率设置

除了我例子中的设置,Laravel 还提供了如下频率设置:

方法 描述
->cron('* * * * *'); 在自定义的 Cron 时间表上执行该任务
->everyMinute(); 每分钟执行一次任务
->everyFiveMinutes(); 每五分钟执行一次任务
->everyTenMinutes(); 每十分钟执行一次任务
->everyFifteenMinutes(); 每十五分钟执行一次任务
->everyThirtyMinutes(); 每半小时执行一次任务
->hourly(); 每小时执行一次任务
->hourlyAt(17); 每小时的第 17 分钟执行一次任务
->daily(); 每天午夜执行一次任务
->dailyAt('13:00'); 每天的 13:00 执行一次任务
->twiceDaily(1, 13); 每天的 1:00 和 13:00 分别执行一次任务
->weekly(); 每周执行一次任务
->monthly(); 每月执行一次任务
->monthlyOn(4, '15:00'); 在每个月的第四天的 15:00 执行一次任务
->quarterly(); 每季度执行一次任务
->yearly(); 每年执行一次任务
->timezone('America/New_York'); 设置时区

更多频率设置,请参考官方文档。

启动调度

进入 shell 中,运行 crontab -e 命令,按 i 键进入编辑模式,添加如下配置:

* * * * * /usr/local/php/bin/php /home/api/admin/artisan schedule:run >> /dev/null 2>&1

其中 /usr/local/php/bin/php 是 php 命令所在目录,/home/api/admin 是 Laravel 项目的根目录

添加后按 ESC 键,退出编辑模式,输入 wq 并回车,保存 crontab 任务,Laravel 的任务调度就启动了。

数据库定时备份功能

数据库定时备份数据,对每个项目来说都是非常有必要的,能够有效地避免删库跑路这种操作。

2fdda3cc7cd98d10fb604a2f2c3fb80e7bec907c.gif

我还是使用调度 command 的方式来完成这个功能,首先在项目根目录运行命令:

php artisan make:command BackupDatabase

该命令创建了一个同名 PHP 文件,被放在了 app/Console/Commands 目录下

BackupDatabase.php 的内容如下:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

class BackupDatabase extends Command
{
    protected $signature = 'db:backup';

    protected $description = 'Backup the database';

    protected $process;

    public function __construct()
    {
        parent::__construct();

        $this->process = new Process(sprintf(
            'mysqldump -u%s -p%s %s > %s',
            config('database.connections.mysql.username'),
            config('database.connections.mysql.password'),
            config('database.connections.mysql.database'),
            storage_path('backups/backup.sql')
        ));
    }

    public function handle()
    {
        try {
            $this->process->mustRun();

            $this->info('The backup has been proceed successfully.');
        } catch (ProcessFailedException $exception) {
            $this->error('The backup process has been failed.');
        }
    }
}

上面的构造函数中,我使用了 Symfony 框架的 Process 组件,这是一个很强大的组件,功能类似于 shell_exec 函数,可以执行 shell 命令。

Process 组件使用文档

我使用 Process 组件执行了 mysqldump 命令,mysqldump 命令的使用格式如下:

mysqldump [options] [db_name [tbl_name ...]]

BackupDatabase.php 运行后会将数据备份在 backups/backup.sql 中,并且当任务执行出错时会记录错误。

最后一步,和之前一样在 app/Console/Kernel.php 中的 schedule 方法中完成对 db:backup 命令的调度:

protected function schedule(Schedule $schedule)
{
    $schedule->command('db:backup')->dailyAt('03:30');
}

因为每天凌晨 03:30 用户活跃度最低,所以选择在该时间备份一次数据。

发布评论
还没有评论,快来抢沙发吧!