前提
Laravel 中任务调度简化了服务器系统中 Cron 的操作,使得 计划任务 的实现更为简便。
这里主要以 Laravel 自带的消息队列进行说明,了解其间运行关系可以让我们更清晰的进行代码实现。
下方代码以 Lumen 9.x 框架进行举例,与 Laravel 应无二致。
中文参考文档:任务调度 - Laravel 9.x
定义任务调度
我们称之为 计划任务 、周期性任务 均可。因为他的目的即是如此。
首先编辑 App\Console\Kernel
类,默认内容如下:
php
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\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 Schedule $schedule
*
* @return void
*/
protected function schedule(Schedule $schedule): void {
// TODO:
}
}
例如我们需要在 每周一凌晨时段 执行业务统计任务(假设统计上一周订单量、销售额)
周期定义可参阅:任务调度 - 调度频率选项
在 schedule
方法中先写上如下代码:
php
protected function schedule(Schedule $schedule): void {
$schedule->job() // 到达指定周期执行的目标任务,此处我们的目的是生产一条消息队列
->withoutOverlapping() // 避免任务重复
->timezone('Asia/Shanghai') // 指定时区
->weeklyOn([1], '03:00'); // 定义时间:每周一 03:00 AM 执行。如果周一、周三、周六皆需要运行则可将第一参数修改为 [1, 3, 6]
}
IDE 会对 job
方法报出警告,因为我们还未添加其对应的消息队列
添加消息队列
Laravel/Lumen 框架中支持多种消息队列驱动:sqs
、database
、redis
、beanstalkd
,我们以 redis
为例。此处假设你已做好 redis
队列相关环境以及配置。
我们在 App\Jobs
中编写一个 WeeklyStatisticsJob
类:
php
<?php
namespace App\Jobs;
class WeeklyStatisticsJob extends Job {
public function __construct() {
}
/**
* Execute the job.
*
* @return void
*/
public function handle(): void {
// TODO:
echo "done\n";
$this->delete();
}
}
如需传递自定义参数至该 Job
类,只需在 __construct
方法中添加形参相应,并传递至成员变量,例如:
php
<?php
namespace App\Jobs;
class WeeklyStatisticsJob extends Job {
private int $supplierId;
private string $beginDate;
private string $endDate;
public function __construct(int $supplier_id, string $begin_date, string $end_date) {
$this->supplierId = $supplier_id;
$this->beginDate = $begin_date;
$this->endDate= $end_date;
}
}
TODO:
部分为实际业务代码,例如调用服务类实现统计并存储于数据库中。
应用消息队列
回到 App\Console\Kernel
类,将消息队列类实例化进 job
方法即可:
php
protected function schedule(Schedule $schedule): void {
$schedule->job(new WeeklyStatisticsJob(1, '2023-11-13', '2023-11-19'))
->withoutOverlapping()
->timezone('Asia/Shanghai')
->weeklyOn([1], '03:00');
}
注意 ,我们为了将其与已有消息队列进行区分,应对其指定单独的队列名称。同样,如果需要单独指定消息队列的连接(使用 database
、sqs
等),还需指定连接名。
所以我们应当修改为:
php
protected function schedule(Schedule $schedule): void {
$schedule->job(
new WeeklyStatisticsJob(1, '2023-11-13', '2023-11-19'),
'weekly_calc_statistics', // 建议指定队列名称,如未指定,默认为 'default'
'redis' // 默认使用 redis 时,此处可忽略
)
->withoutOverlapping()
->timezone('Asia/Shanghai')
->weeklyOn([1], '03:00');
}
至此,我们的周期任务就已编写完成,但对于测试来说,周期过于漫长,我们可以将 每周一凌晨三点 修改为每分钟,或者每 5-10 分钟进行测试。
即,将 weeklyOn([1], '03:00')
修改为 everyMinute()
或 everyFiveMinute()
等方法。
运行测试
我们首先应当运行上方编写的队列监听:weekly_calc_statistics
。
在项目根目录中执行下方命令:
bash
# 如未指定队列名,可忽略 queue 参数
php artisan queue:work --queue=weekly_calc_statistics
随后在 另一 命令窗口中执行:
bash
php artisan schedule:work
稍等片刻,周期任务将生产一条消息队列放入 redis
中,随后可在消息队列命令窗口中看到该队列被消费提示。