在 laravel 中使用 redis 队列

我们实现的效果为:将一些业务逻辑转化为任务(job),然后启用队列功能,而且是 redis 的队列,同时使用 laravel 官方扩展 Horizon 实现队列的监控。将复杂耗时的工作后台处理,提高前台用户的使用体验。

1. 安装 redis

注意不要使用国内 composer 源,官方的是最全最新的(什么阿里云镜像源反向升级了解一下)

1
composer require predis/predis

修改环境变量 QUEUE_CONNECTION 的值为 redis,并指定我们将使用 predis 作为请求 Redis 的类库:

.env

1
2
3
4
5
6
7
8
.
.
.
QUEUE_CONNECTION=redis
REDIS_CLIENT=predis
.
.
.

2. 生成任务类

使用以下 Artisan 命令来生成一个新的队列任务:

1
$ php artisan make:job EnterpriseExport

该命令会在 app/Jobs 目录下生成一个新的类:

app/Jobs/EnterpriseExport.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php

namespace App\Jobs;

use App\Admin\Controllers\Api\StaticFun;
use App\Models\Asset;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;

class GetAssetsRemainDate implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// 拿到全部资产信息
$assets = Asset::all();

// 开始循环计算资产剩余质保时间并更新
foreach ($assets as $key => $value) {
// 计算
$warranty_info = StaticFun::get2DateInfo($value->warranty_start, $value->warranty_end);
// 拿取
$warranty_remain = $warranty_info['warranty_remain'];
// 更新写入
DB::table('asset')->where('id', $value->id)->update(['warranty_remain' => $warranty_remain]);
}
}
}

还有一点需要注意,我们将会在模型监控器中分发任务,任务中要避免使用 Eloquent 模型接口调用,如:create(), update(), save() 等操作。否则会陷入调用死循环 —— 模型监控器分发任务,任务触发模型监控器,模型监控器再次分发任务,任务再次触发模型监控器… 死循环。在这种情况下,使用 DB 类直接对数据库进行操作即可。

3. 任务分发

前往控制器或者观察者,去插入这项任务。

SettingController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.
.
.
class SettingController extends Controller
{
// 展示
public function index(Content $content)
{
return $content
->header('系统设置')
->description('亲自参与到系统的个性化管理')
->body(function (Row $row) {
$row->column(12, Setting::index());
});
}

// 刷新资产质保剩余时间
public function warrantyRefresh()
{
dispatch(new GetAssetsRemainDate); <-- HERE

return redirect('/admin/setting');
}
}
.
.
.

4. 安装 Horizon

使用 Composer 安装:

1
$ composer require laravel/horizon

安装完成后,使用 vendor:publish Artisan 命令发布相关文件:

1
$ php artisan vendor:publish --provider="laravel\Horizon\HorizonServiceProvider"

分别是配置文件 config/horizon.php 和存放在 public/vendor/horizon 文件夹中的 CSS 、JS 等页面资源文件。

Horizon 是一个监控程序,需要常驻运行,我们可以通过以下命令启动:

1
$ php artisan horizon

至此安装完毕,浏览器打开 larabbs.test/horizon 访问控制台

5. 安装 Supervisor

Supervisor 是一个用于 Linux 操作系统的进程监视器。如果 Horizon 进程被退出或终止,Supervisor 将自动重启你的 Horizon 进程。如果要在 Ubuntu 上安装 Supervisor,你可以使用以下命令。如果你不使用 Ubuntu,也可以使用操作系统的包管理器安装 Supervisor:

1
sudo apt-get install supervisor

6. 配置 Supervisor

Supervisor 配置文件通常存储在 /etc/supervisor/conf.d 目录下。在此目录中,你可以创建任意数量的配置文件,这些配置文件会告诉 supervisor 如何监视你的进程。例如,让我们创建一个 horizon.conf 文件,它启动并监视一个 horizon 进程:

1
2
3
4
5
6
7
8
9
10
11
12
[program:horizon]
process_name=%(program_name)s
directory=/data/wwwroot/yanji.wkarrow.top
command=/usr/local/php/bin/php artisan horizon
autostart=true
autorestart=true
user=root
stopasgroup=true
killasgroup=true
redirect_stderr=true
stdout_logfile=/data/wwwroot/yanji.wkarrow.top/horizon.log
stopwaitsecs=3600

注意:要确保 stopwaitsecs 的值大于运行时间最长的任务所消耗的秒数。否则,Supervisor 可能会在工作完成前终止任务。
注意:编辑文件之前,请使用 root 权限进行操作,否则无法保存(针对 homestead 环境下的操作)

7. 启动 Supervisor

在将新代码部署到服务器时,你需要终止 Horizon 主进程,以便进程监视器重新启动它并接收代码的更改。

1
php artisan horizon:terminate

创建了配置文件后,可以使用以下命令更新 Supervisor 配置并启动进程:

1
2
3
4
5
sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start horizon