zoukankan      html  css  js  c++  java
  • laravel queue队列使用

    一篇文章:

    laravel中的队列服务跟其他队列服务也没有什么不同,都是最符合人类思维的最简单最普遍的流程:有一个地方存放队列信息,一个PHP进程在运行时将任务写入,另外一个PHP守护进程轮询队列信息,将达到执行要求的任务执行并删除。由于PHP是url驱动的同步语言,本身是阻塞的,所以laravel提供一个守护进程工具来查询并执行队列信息也就不足为奇了。

    Laravel的queue配置文件是 /app/config/queue.php,在 Default Queue Driver 这一项中,可以选择"sync", "beanstalkd", "sqs", "iron", "redis" 五种驱动器。

    配置文件:

    queue.php

    'redis' => array(
    'driver' => 'redis',
    'queue' => 'default',
    ),

    database.php:

    /*
    |--------------------------------------------------------------------------
    | Redis Databases
    |--------------------------------------------------------------------------
    |
    | Redis is an open source, fast, and advanced key-value store that also
    | provides a richer set of commands than a typical key-value systems
    | such as APC or Memcached. Laravel makes it easy to dig right in.
    |
    */

    'redis' => array(

    'cluster' => false,

    'default' => array(
    'host' => '127.0.0.1',
    'port' => 6379,
    'database' => 'laravelFirst',
    ),

    ),

    1. sync是本地调试用的同步驱动器

    2. beanstalkd 是一个专业队列服务驱动器:http://kr.github.io/beanstalkd/

    3. sqs和iron是国外第三方队列服务

    4. 最后一项redis给了我们一个使用redis的理由,这样我们顺便把缓存服务和session服务全部迁移到redis上了。

    0. 顺便说一句,session驱动器千万别用mysql,处理时间1S不是梦,哎,看谁呢,说的就是你,1S哥!

    队列服务需要专门新建任务类,作为独立类,他们不需要继承类,因为队列里的任务在执行的时候,是由PHP守护进程来独立调用的,当然如果你要use一下别的类再调用,也不会出错。之前我把很多额外服务独立到了一个单独的文件夹 /app/services 里,比如输入信息验证 validator,特殊安全验证模块等,这次queue类们就位于其中。

    queue的使用非常简单,下面就是一个简单的示例:

    use Queue;
    Queue::push('CurlJsonQueue', [
     'url' => $url,
     'json' => $json
    ]);

    这就是一个标准的queue压入流程了。当然,在这里我把CurlJsonQueue类放到了services根目录下,这个目录已经被我注册到composer.json的"autoload"的"classmap"中,是位于顶层命名空间中的,可以直接调用,如果需要调用非顶层命名空间,是可以写 AppOOXX 的。我们的系统需要大量和微信服务器交互,所以就独立出来了这个类。

    <?php

    class CurlJsonQueue extends BaseController{

     public function fire($job, $data)
     {
      $url = $data['url'];
      $json = $data['json'];

      parent::base_post_curl($url, $json);

      $job->delete();
     }
    }

    这个类默认的方法是 fire() ,参数也是固定的两个 $job 和 $data,由于我在BaseController中封装了post的curl模块,所以就调用了一下。另外这里还有一个小坑,当时写base_post_curl() 的时候用的protected,导致use BaseController无效,必须继承。

    通过执行上面的代码,queue中就被放入了一个新的任务,laravel通过下面的命令开启守护进程:

      

    php artisan queue:listen

    然后守护进程就开始处理队列了。此代码中的PHP命令和artisan文件的路径请自行调整。

    大家可能注意到了,我们要使用的这个队列系统用到了redis和PHP命令行,如果在测试环境,加个开机启动甚至是手动启动都可以,但是在生产环境就需要更稳固的工具来守护这两个程序,我们用的是supervisor,关于supervisor的安装配置大家可以参考这篇文章: http://blog.segmentfault.com/qianfeng/1190000000532561 注意,文章里有小坑请自行去踩。。。

    OK,全部配置好之后,跑起来redis和PHP命令行,整个系统就开始愉快地运行啦~

    使用感受:

    队列服务超好用,之前一次和app的交互流程需要6-7S,异步以后降低到2S以内,基本就是传输时间和PHP代码运行时间了,耗时的特殊操作已经异步了。不过队列服务默认1S开一个进程检查一次redis中有没有可以运行的服务,在阿里云服务器上,大约能占到单核的10%,消耗略大,而且队列处理时间相对较长,因为没有了之前同步时候的文件加载福利。不过如果有多个任务,PHP进程是会连续执行的,不会1S执行一个的啦。

    下面说说坑:

    1. 由于queue核心类使用了一个特殊函数,导致没有明确类型的变量会以单元素数组的形式存进json,再存进redis。解决办法就是在每一个要放进去的数据前面加上 ''. 。上面的$url和$json由于都已经在前面用引号进行了类型申明,故没做这一步操作。

    2. 如果要传递url给队列,系统queue类会在每一个 / 前面加上两个 \ 。这对于一些特殊操作可能会造成致命影响。(开玩笑,有上面那个致命么!)

    ------------------

    我的使用,在app/service存放一个文件:

    class SendEmail{
        public function send($job,$data)
        {
            
            system(SEND_MAIL);
            
            $job->delete();
            
        }
    }

    某个地方放入队列:

    Queue::push('SendEmail@send', array('message' =>"hello world"));

    有时候queue default会有2个queue:

    一篇文章:

    ---

    利用Redis可以很方便的实现一个任务队列,但是在Laravel中,Redis的队列总会出现一个任务多次执行的问题。究其原因是它写死了reserved的时长,也就是如果1分钟后任务没有执行完成,那么这个任务就会被重新放回队列。下面是队列的简单使用和执行原理。

    设置

    设置队列使用Redis非常容易,在app/config/queue.php中配置

    ...
    'default' => 'redis',
    ...
    'connections' => array(
        ...
        'redis' => array(
            'driver' => 'redis',
            'queue'  => 'waa',
        ),
    ),

    即可。

    使用

    使用时不需要多配置,只要写好Queue类和其fire方法,在需要的位置出队即可。具体方法可以看这里

    class SendEmail {
    
        public function fire($job, $data)
        {
            //
            $job->delete();
        }
    
    }
    
    Queue::push('SendEmail@send', array('message' => $message));

    流程

    Laravel利用artisan命令来执行出队操作,然后进行任务的执行。方法调用如下:

    1. artisan queue:work
    2. WorkerCommand:fire()
    3. Worker:pop()
    4. Worker:getNextJob()
    5. RedisQueue:pop()
    6. Worker:process()

    我遇到的问题就在这里,在RedisQueue:pop()方法中,有这样一句:

    $this->redis->zadd($queue.':reserved', $this->getTime() + 60, $job);

    这里将当前执行的任务放到另外一个reserved队列中,超时时间是60s。也就是说,如果60s后这个任务没有被删除掉,则任务会重新被放入队列中来。因此,在实际的使用过程中,任务很可能被多次执行。解决的办法是

    class SendEmail {
    
        public function fire($job, $data)
        {
            $job->delete();
            // job 
        }
    
    }

    即先删除这个任务,再开始执行任务

    参考:http://yansu.org/2014/04/11/redis-queue-in-laravel.html

    supervisor管理queue:http://yansu.org/2014/03/22/managing-your-larrvel-queue-by-supervisor.html

  • 相关阅读:
    (四)自定义多个Realm以及Authenticator与AuthenticationStrategy
    (三)自定义Realm
    (二)shiro之jsp标签
    (一)shiro简介和用户登录demo及角色管理
    解决Cannot change version of project facet Dynamic web module to 2.5(转)
    (十二)easyUI之表单和验证完成登录页面
    (十一)springmvc和spring的整合
    (十)springmvc之文件的处理
    (九)springmvc之json的数据请求(客户端发送json数据到服务端)
    (九)springmvc之json的处理(服务端发送json数据到客户端)
  • 原文地址:https://www.cnblogs.com/youxin/p/4206972.html
Copyright © 2011-2022 走看看