laravel 完成Job定时任务解决php在某一时间执行一次某个自定义任务。
已知有多个活动且每个用户只能关注一个活动,活动表与用户表的关系为一对多【即“活动表”一条数据 关联 多条“用户表”数据】。现在要求活动开始前10分钟对关注该活动的用户进行消息推送提醒。
注意:Laravel框架版本号不一定要和我的相同,该框架其他版本也有这个功能,更具自己的实际需要来。
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 |
id(用户表id) | name(用户名) | email(电子邮箱) | activity_id(订阅活动表关联id) | is_send(是否执行推送 0:否,1:是) |
---|---|---|---|---|
1 | 李琳 | [email protected] | 1 | 1 |
2 | 王涛 | [email protected] | 1 | 1 |
3 | 张英 | [email protected] | 2 | 0 |
4 | 孙鹏 | [email protected] | 3 | 0 |
真实数据库会设计成3张表,即 用户表、活动表、用户活动中间表。我这里为了简化演示流程省略中间表,仅设计2张表
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配置项
REDIS_HOST=31.155.243.118 #你服务器的ip地址
REDIS_PASSWORD=aaaaa #你redis的密码,如果你没设置 就留空(留空的话=号后面别跟空格)
REDIS_PORT=6379 #redis端口号
REDIS_DB=13 #redis数据库。redis默认有0-15个数据看。你用哪个就填哪个,我用的是13号数据库没有这个默认就是0号数据库(这个不改也行,个人喜好)
#添加如下配置
QUEUE_DRIVER=redis #默认链接队列名称 他的配置项在.\config\queue.php ->'default'
REDIS_QUEUE=queue_01 #redis队列 他的配置项在.\config\queue.php ->'connections'=>'redis'=>'queue' 这里的名字你自定义就好,这个值最终用在 任务将被发送到「testlist」队列 Job::dispatch()->onQueue('testlist');
#修改如下配置
#默认代码长这样QUEUE_CONNECTION=sync
QUEUE_CONNECTION=redis #默认队列连接名称 我们用的是 .\config\queue.php ->‘connections’=>'redis'配置项。所以这里写redis就好。
5.创建任务类 例如我想创建一个延时发送邮件的任务类(类名自定义,想叫啥随你喜欢):php artisan make:job DelaySendEmail
php artisan make:job 任务类名称(你自己定义)
6.分别创建控制器和控制器相对应的路由。格式:php artisan make:controller 你控制器的名称
php artisan make:controller ActivityController
7.活动控制器代码
namespace App\Http\Controllers;
use App\Jobs\DelaySendEmail;
use App\Models\Activity;
use App\Models\Userinfo;
use Illuminate\Http\Request;
/**
* 活动控制器
* Class ActivityController
* @package App\Http\Controllers
*/
class ActivityController extends Controller
{
/**
* 关注活动
* @param Request $request
*/
public function attention(Request $request){
$user_id = (int)$request->user_id;//用户id
$activity_id = (int)$request->activity_id;//活动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,//成功状态码 0:否,1:是
];
}
//更新当前用户与活动的关联关系
$update = Userinfo::where('id',$user_id)->update(['activity_id'=>$activity_id]);
//为啥要用->onQueue('queue_01') 执行队列名,因为如果你有多个项目都要部署job,不指定队列名可能导致A项目拿到了B项目的Job
$this->dispatch((new DelaySendEmail($user_id, $time_difference))->onQueue('queue_01'));//方式A:
//方式B:$this->dispatch((new DelaySendEmail($user_id, $time_difference))->onQueue('queue_01')->delay(now()->addSeconds($time_difference))); 代码解释:调用延迟任务进行邮件分发,并使用链式调用->addSeconds($time_difference)设计延迟执行的时间。
$date = date('Y-m-d H:i:s');
dump('等待执行',"延迟{
$time_difference}秒后执行。","活动开始时间:{
$activity['start_time']}","当前时间:{
$date}");
}
}
8.延迟发送邮件Job类
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
* @package App\Jobs
*/
class DelaySendEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $userinfo;//用来保存用户信息
/**
* 构造函数
* Create a new job instance.
* DelaySendEmail constructor.
* @param (int)$user_id 用户id
* @param (int)$time_difference 执行延时任务的时间(秒)
*/
public function __construct($user_id,$time_difference)
{
$userinfo = Userinfo::find($user_id);//获取用户信息
$this->userinfo = $userinfo;//赋值用户信息
$this->delay(now()->addSeconds($time_difference));//在 $time_difference 秒 后执行延时任务
//$this->delay(now()->addSeconds($time_difference))写在外面的活动控制器中也可以,如:$this->dispatch((new DelaySendEmail($user_id, $time_difference))->delay(now()->addSeconds($time_difference)));
}
/**
* 执行业务控制器
* 用来执行延时任务的具体业务逻辑
*/
public function handle()
{
//向指定用户发送邮件的代码业务逻辑
//.....
//判断邮件是否发送成功
//.....假设已经发送成功
//修改用户数据模型将状态改为1:已发送
$update = Userinfo::where('id',$this->userinfo->id)->update(['is_send'=>1]);
}
}
9.使用artisan 命令 执行队列监听 注意:这里的Dos窗口不能关闭,如果关闭或Ctrl+C键退出命令都会导致监听不在生效。
-执行监听:命令php artisan queue:work
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
如果这片文章帮助到了你,请帮我点个赞,您的支持是对我最大的鼓励!