zoukankan      html  css  js  c++  java
  • 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 AppListeners;
    
    use IlluminateContractsQueueShouldQueue;
    
    class MyListener implements ShouldQueue
    {
        /**
         * 任务将被推送到的连接名称.
         *
         * @var string|null
         */
        public $connection = 'redis';
    
        /**
         * 任务将被推送到的列队名称.
         *
         * @var string|null
         */
        public $queue = 'queue_name';
    
        /**
         * 任务被处理之前的延迟时间(秒)
         *
         * @var int
         */
        public $delay = 60;
    }

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

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

  • 相关阅读:
    Flash代码执行原理与性能优化笔记
    服务器自建git
    列表删除
    mysql的隔离级别实践
    py2和py3的字符编码
    Django外键(ForeignKey)操作以及related_name的作用
    python多个装饰器嵌套
    git diff使用
    .gitignore 只包含几个文件
    三层菜单字典练习
  • 原文地址:https://www.cnblogs.com/ddcoder/p/13436376.html
Copyright © 2011-2022 走看看