在laravel中使用redis队列,发送邮件

1,971 阅读3分钟

一、驱动的选择

使用数据库驱动

优点:不用安装其他的啦里啦杂的东西,直接用

缺点:要进行数据库迁移,产生工作数据表

.env文件中设置队列驱动为数据库
php artisan queue:table
创建队列用到的数据表
php artisan queue:table
执行migrate
# 会新建 database/migrations/{timestamp}_create_jobs_table.php 文件
php artisan migrate
.env文件示例
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=database

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=smtp.exmail.qq.com // QQ企业邮箱
MAIL_PORT=25 // 使用25端口
MAIL_USERNAME=xxxx@xxx.com // 发件人邮箱
MAIL_PASSWORD=*** // 密码或授权码
MAIL_ENCRYPTION=tls

使用redis驱动

安装redis
windows下

windows下载地址,下载运行安装即可。

64bit-redis
打开一个cmd窗口使用cd命令切换目录到C:\redis运行redis-server.exe redis.windows.conf

Linux下
# 看看有木有密码
/etc/redis.conf port6379
项目中使用 Composer 安装依赖
composer require "predis/predis:~1.0" 
.env文件示例
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=honeybot
DB_USERNAME=root
DB_PASSWORD=root

BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=smtp.exmail.qq.com
MAIL_PORT=465 // 使用465端口
MAIL_USERNAME=xxx@xxx.com
MAIL_PASSWORD=***
MAIL_ENCRYPTION=ssl // 使用465端口

二、失败任务记录(可有可无,看你自己想不想记录)

有时候队列中的任务会失败。Laravel 内置了一个方便的方式来指定任务重试的最大次数。当任务超出这个重试次数后,它就会被插入到 failed_jobs 数据表里面。我们可以使用queue:failed-table命令来创建 failed_jobs 表的迁移文件

# 会新建 database/migrations/{timestamp}_create_failed_jobs_table.php 文件
 php artisan queue:failed-table

接着使用 migrate Artisan 命令生成 failed_jobs 表:

php artisan migrate

三、创建任务

创建任务 = 搞一个生产者 = (其实就是写一个在队列中你想执行的业务逻辑),名字随意取,但最好遵守命名规范。

这个生成的文件大概分2部分:一是__construct() 构造方法 ;二是handle 队列执行方法(意思就是在队列执行的时候,就用你这里面写的代码)

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

# 该命令会在 app/Jobs 目录下生成一个新的类
php artisan make:job SendReminderEmail

新生成的类:pp/Jobs/SendReminderEmail.php

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Util\L;
use Mail;

class SendReminderEmail implements ShouldQueue
{
   use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

   protected $orderInfo;
   protected $email;

   /**
    * Create a new job instance.
    *
    * @return void
    */
   public function __construct($orderInfo, $email)
   {
       $this->orderInfo = $orderInfo;
       $this->email = $email;
   }

   /**
    * Execute the job.
    *
    * @return void
    */
   public function handle()
   {
       // Mail::send()的返回值为空,所以可以其他方法进行判断
       Mail::send('emails.new_order_mail',
           [
               'order_number' => $this->orderInfo['order_number'],
               'goods_name' => $this->orderInfo['goods_name'],
               'goods_color' => $this->orderInfo['goods_color'],
               'goods_num' => $this->orderInfo['goods_num']
           ],
           function ($message) {
               $to = $this->email;
               $message->to($to)->subject('【xx商城新订单通知】');
           });
       // 返回的一个错误数组,利用此可以判断是否发送成功
       if (count(Mail::failures()) >= 1) {
           L::email("订单:" . $this->orderInfo['order_number'] . "的 " . $this->email . " 邮件通知发送失败");
       }
   }
}

任务分发

生产者,在控制器内使用dispatch方法调用即可
<?php

namespace App\Api\Controllers\Mail;
use App\Api\Controllers\BaseController;
use App\Repositories\Order\OrderRepository;

class MailController extends BaseController
{
   public function __construct(OrderRepository $order)
   {
       $this->order = $order;
   }

   public function send()
   {
       $header_id = 3691;
       $this->order->sendMail($header_id);
   }
}
定义发送邮件的类,分发任务
/**
    * @param $header_id
    * @return mixed
    * 发送邮件 队列
    */
   public function sendMail($header_id)
   {
       try {
           // 邮件内容
           $orderInfo = OrderDetail::select('order_number', 'order_lines.*')
               ->leftJoin('order_headers', 'order_headers.uid', '=', 'header_id')
               ->where('header_id', $header_id)
               ->first();
           $order['order_number'] = $orderInfo->order_number;
           $order['goods_name'] = '嘻嘻' . $orderInfo->combo;
           $order['goods_color'] = $orderInfo->color;
           $order['goods_num'] = $orderInfo->quantity;
           L::email("订单:" . $orderInfo->order_number);
           // 邮件地址列表
           $emailList = ["xxx@qq.com", "xxx@163.com"]; // 测试 邮件地址
           // 推送任务入队列
           foreach ($emailList as $email) {
               dispatch(new SendReminderEmail($order, $email));
           }
       } catch (\Exception $e) {
       }
   }
新建邮件发送模板
// /resource/views/emailsnew_order_mail.blade.php
<div>你好,小程序商城收到新订单。</div>
<div>收货地址: {{$address}}</div>
<div>订单号: {{$order_number}}</div>
<div>商品名称: {{$goods_name}}</div>
<div>商品颜色: {{$goods_color}}</div>
<div>数量: {{$goods_num}}</div>

四、开始测试

开始之前,我们需要在命令行启动队列系统,队列在启动完成后会进入监听状态

php artisan queue:listen

五、运行队列进程

php artisan queue:work

六、配置 Supervisor

测试完成后,邮件发送成功,表示你已经成功啦~

but,如果是在生产环境,就不可能是像开发时启动一下队列发送一次,肯定是要有一定的监听和触发机制,所以Supervisor登场了。

supervisord 是进程管理的服务端,常驻进程辅助干活 supervisorctl 是客户端,用来执行查看、加载等命令

具体请看配置 Supervisor,linux下的队列进程管理服务端