前言
laravel 完成Job定时任务解决php在某一时间执行一次某个自定义任务。
目标需求
已知有多个活动且每个用户只能关注一个活动,活动表与用户表的关系为一对多【即“活动表”一条数据 关联 多条“用户表”数据】。现在要求活动开始前10分钟对关注该活动的用户进行消息推送提醒。
开发环境
Linux+Nginx+mysql+php7.3+Redis+Laravel 6.2.* + Supervisor管理器(用来做进程守护的工具)
注意:Laravel框架版本号不一定要和我的相同,该框架其他版本也有这个功能,更具自己的实际需要来。
数据模型
活动表(activity)
id(活动表id) | name(活动名称) | start_time (活动开始时间) |
---|
1 | 篮球活动比赛直播 | 2021-07-23 19:30:00 | 2 | 网球活动比赛直播 | 2021-07-25 09:30:00 | 3 | 游泳活动比赛直播 | 2021-07-28 15:00:00 |
用户表(userinfo)
id(用户表id) | name(用户名) | email(电子邮箱) | activity_id(订阅活动表关联id) | is_send(是否执行推送 0:否,1:是) |
---|
1 | 李琳 | 741753@aq.com | 1 | 1 | 2 | 王涛 | 278899@aq.com | 1 | 1 | 3 | 张英 | 1533245@cmcc.com | 2 | 0 | 4 | 孙鹏 | 36589@152.com | 3 | 0 |
真实数据库会设计成3张表,即 用户表、活动表、用户活动中间表。我这里为了简化演示流程省略中间表,仅设计2张表
业务流程
- 创建活动
- 程序更具活动 开始的时间减10分钟 创建Job定时推送任务(指定时间且仅执行一次的任务)
- 用户关注某个活动,建立与活动表的关联关系
- 系统到达某活动设置的执行推送时间,更具活动id获取关注该活动的用户,并执行推送服务。
- 执行推送服务后,改变用户表的推送状态标识。
- End;
代码部分
1.首先下载laravel6.2.*框架
composer create-project --prefer-dist laravel/laravel JobTaskDemo 6.2.*
2.进入项目内下载redis依赖扩展包 注意:要是你下的redis依赖装不上就查下你框架的版本或者php的版本与你当前装的redis拓展要求是否相符,不相符的去安装对应版本的redis拓展。
composer require predis/predis
3.创建数据模型 用户表、活动表(详情字段见上述表格)并完成表格迁移。
4.修改.env文件配置项,我们这里使用redis。(如果你的.env中有下述配置项则进行修改,没有则手动新增)
REDIS_HOST=31.155.243.118
REDIS_PASSWORD=aaaaa
REDIS_PORT=6379
REDIS_DB=13
QUEUE_DRIVER=redis
REDIS_QUEUE=queue_01
QUEUE_CONNECTION=redis
5.创建任务类 例如我想创建一个延时发送邮件的任务类(类名自定义,想叫啥随你喜欢):php artisan make:job DelaySendEmail
php artisan make:job 任务类名称(你自己定义)
6.分别创建控制器和控制器相对应的路由。格式:php artisan make:controller 你控制器的名称
php artisan make:controller ActivityController
配置该控制器相应的路由
7.活动控制器代码
<?php
namespace App\Http\Controllers;
use App\Jobs\DelaySendEmail;
use App\Models\Activity;
use App\Models\Userinfo;
use Illuminate\Http\Request;
class ActivityController extends Controller
{
public function attention(Request $request){
$user_id = (int)$request->user_id;
$activity_id = (int)$request->activity_id;
$activity = Activity::find($activity_id);
$time = time();
$start_time = strtotime($activity['start_time']);
$time_difference = bcsub($start_time,$time,0);
if($time_difference < 0) {
return [
'code'=>403,
'mssage'=>'当前活动已结束',
'success'=>0,
];
}
$update = Userinfo::where('id',$user_id)->update(['activity_id'=>$activity_id]);
$this->dispatch((new DelaySendEmail($user_id, $time_difference))->onQueue('queue_01'));
$date = date('Y-m-d H:i:s');
dump('等待执行',"延迟{$time_difference}秒后执行。","活动开始时间:{$activity['start_time']}","当前时间:{$date}");
}
}
8.延迟发送邮件Job类
<?php
namespace App\Jobs;
use App\Models\Activity;
use App\Models\Userinfo;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class DelaySendEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $userinfo;
public function __construct($user_id,$time_difference)
{
$userinfo = Userinfo::find($user_id);
$this->userinfo = $userinfo;
$this->delay(now()->addSeconds($time_difference));
}
public function handle()
{
$update = Userinfo::where('id',$this->userinfo->id)->update(['is_send'=>1]);
}
}
9.使用artisan 命令 执行队列监听 注意:这里的Dos窗口不能关闭,如果关闭或Ctrl+C键退出命令都会导致监听不在生效。
-执行监听:命令php artisan queue:work
- 如何守护进程:所以你需要开启守护进程模式:在服务器上安装Supervisor管理器 配合命令一起使用实现守护进程
- 如何重启队列:修改了队列的代码,但是不生效怎么办?
如果改动了对列的代码需要执行下php artisan queue:restart 使队列进行重启。
运行结果展示
1.设置活动开始时间 2.运行监听队列命令 3.用户请求延迟任务接口前的状态
4.用户请求接口,也就是说如果最后的用户表的更新时间为“2021-07-27 11:04:57”左右即表示我们运行成功。
5.延迟任务执行完毕后的结果(差了2秒,可能是代码运行或者服务器延迟所造成的)
总结
注意: 1.运行命令时,注意PHP的禁用函数是否开启。 2. env文件中 REDIS_QUEUE 一定要指定!不然当多个项目有Job需求时,你在一个项目中跑 php artisan queue:work 会拿到另外一个项目的 job,这样就会导致一些不必要的异常,因为在反序列化的过程中会找不到对应的类。 原因:.\config\queue.php 中’connections’配置的默认 queue 都是 default
如果这片文章帮助到了你,请帮我点个赞,您的支持是对我最大的鼓励!
|