Laravel入坑指南(11)——列队

很高兴,我们来到了Laravel入坑指南的第11篇。这一系列的文章已经接近尾声了,在这一节里面,我们一起讨论列队的用法。

列队,顾名思义,将需要处理的任务一个一个排好队,等待处理程序来处理。这机的列队机制,适用于需要异步处理的任务。

所以在这里,我们首先需要关注三个问题:谁来投递任务?将任务投递到哪里?谁来处理任务?

 

1、谁来投递任务?

在Laravel里,任务的投递和处理,都是由JOB类负责的。而且哪个JOB类投递的任务,就由那个JOB类进行处理(划重点)。首先,执行以下命令,就可以创建自己的JOB类:

php artisan make:job ProcessMyJob

这时,就会创建出/app/Jobs/ProcessMyJob.php。在这个类中,有一个构造函数,以及一个handle参数。构造函数的参数,就是分发任务时指定的参数,在获取任务实例化JOB时,就将列队中的任务参数传递进来;handle函数则是处理函数。

有了个这JOB类之后,我们可以在我们需要投递任务的地方(比如某个Controller里)进行投递:

//投递到默认列队,默认列队队名由配置文件决定,一般叫作default
ProcessMyJob::dispatch("参数");

//投递到名字为default2的列队
ProcessMyJob::dispatch("参数")->onQueue("myqueue");

现在问题来了,默认列队和名为myqueue的列队分别在哪里呢?

 

 

2、将任务投递到哪里?

关于这个问题,取决于配置文件/config/queue.php。在这个配置文件中,我们需要关注两个配置项:default和connections。

2.1 connections

'connections' => [

        'sync' => [
            'driver' => 'sync',
        ],

        'database' => [
            'driver' => 'database',
            'table' => 'jobs',
            'queue' => 'default',
            'retry_after' => 90,
        ],

        'beanstalkd' => [
            'driver' => 'beanstalkd',
            'host' => 'localhost',
            'queue' => 'default',
            'retry_after' => 90,
            'block_for' => 0,
        ],

        'sqs' => [
            'driver' => 'sqs',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
            'queue' => env('SQS_QUEUE', 'your-queue-name'),
            'region' => env('AWS_REGION', 'us-east-1'),
        ],

        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => env('REDIS_QUEUE', 'default'),
            'retry_after' => 90,
            'block_for' => null,
        ],

    ],

这个配置项,让Laravel可以用同步、数据库、beanstalkd、亚马逊sql以及redis的方法来保存列队数据。当然,博主个人建议用redis来进行存储,所以下面所有的示例也会以redis作为例子进行讨论。我们在第6节中,已经配置完redis。如果还不明白的同学,请返回查看第6节

根据上面所示connections配置项中,我们只需在/.evn文件中添加一个配置项:

REDIS_QUEUE=default

这一项就是指定上面所说的默认列队名字。当任务投递时没有指定名字,就会投递到这个名下的列队中。

2.2  default

这一项则是指定了名为default的列队,用哪种方式保存列队数据。因为上面的例子中,我们要把任务投递到名字为myqueue的列队中,所以我们还需要一个名为myqueue的列队。所以我们的配置如下:

在/.env配置文件中QUEUE_CONNECTION的值为redis:

QUEUE_CONNECTION=redis

到这里,我们知道任务由谁投递的,任务保存在哪里。那么在指定的地方投递完任务后,由谁调用JOB的handle函数,对任务进行处理呢?就是我们接下来关注的重点。

 

 

3、谁来处理任务?

在这里,我们需要操作系统长驻进程,一直监视着列队是否有任务要处理。所以我们还是离不开php的命令行。我们可以用以下方式启动监视进程:

#启动对所有列队的监听
php artisan queue:work

#启动对default和myqueue的监听,default列队的优先级会高于myqueue的优先级
php artisan queue:work --queue=default,myqueue

#在redis连接上启动对email列队的监听
#这种情况就不需要在配置文件中写email列队的配置项
php artisan queue:work redis --queue=email

#列队清空后就退出
php artisan queue:work --stop-when-empty

#启动两个监听进程,两个列队没有优先级关系
php artisan queue:work --queue=default
php artisan queue:work --queue=myqueue

被监听的列队,一旦有任务存在,监听进程就会实例化对应的JOB类,并调用handle函数进行处理。

 

 

4、万一监听进程挂了,怎么办?

为了解决这个问题,官方文档介绍了可以用Linux下的supervisor进行进程守护。现在让我们简单探讨

在CentOS下安装supervisor只需要执行:

yum install supervisor

安装完成后在就会创建出/etc/supervisord.conf配置文件和/etc/supervisord.d目录。其中,配置文件中有以下配置项:

[include]
files = supervisord.d/*.ini

所有的进程守护配置文件都存放在/etc/supervisord.d路径中。

安装完成后,我们需要创建启动脚本/etc/init.d/supervisor

#!/bin/sh

# Source init functions
. /etc/rc.d/init.d/functions

prog="supervisord"

prefix="/usr/local"
exec_prefix="${prefix}"
prog_bin="${exec_prefix}/bin/supervisord"
PIDFILE="/var/run/$prog.pid"

start()
{
       echo -n $"Starting $prog: "
       daemon $prog_bin -c /etc/supervisord.conf --pidfile $PIDFILE
       [ -f $PIDFILE ] && success $"$prog startup" || failure $"$prog startup"
       echo
}

stop()
{
       echo -n $"Shutting down $prog: "
       [ -f $PIDFILE ] && killproc $prog || success $"$prog shutdown"
       echo
}

case "$1" in

 start)
   start
 ;;

 stop)
   stop
 ;;

 status)
       status $prog
 ;;

 restart)
   stop
   start
 ;;

 *)
   echo "Usage: $0 {start|stop|restart|status}"
 ;;

esac

接下来,让我们进行一下测试,看看supervisor是否有效。创建一个配置文件:

/etc/supervisord.d/myqueue.ini

[program:myqueue]
process_name=%(program_name)s_%(process_num)02d
command=php /www/wwwroot/default/artisan queue:work
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/tmp/test.out.log

创建完成后,执行

/etc/init.d/supervisor restart

会发现我们的列队监视进程已经启动,并且在我们kill掉进程时会自动重启。

到这里,我们列队的话题大功告成。

 

 

最后我们关注上一节遗留下的最后一个问题,关于事件Listener处理。如里我们需要以列队进行事件的处理,我们只需要让侦听器实现ShouldQueue接口即可:

<?php
namespace App\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;

class MyListener implements ShouldQueue
{
    /**
     * 任务将被推送到的连接名称.
     *
     * @var string|null
     */
    public $connection = 'redis';

    /**
     * 任务将被推送到的列队名称.
     *
     * @var string|null
     */
    public $queue = 'queue_name';

    /**
     * 任务被处理之前的延迟时间(秒)
     *
     * @var int
     */
    public $delay = 60;
}

 

 

---------------------------  我是可爱的分割线  ----------------------------

最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

 

posted on 2020-08-04 23:43  咚..咚  阅读(442)  评论(0编辑  收藏  举报

导航