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"中,是位於頂層命名空間中的,可以直接調用,如果需要調用非頂層命名空間,是可以寫 App\OOXX 的。我們的系統需要大量和微信服務器交互,所以就獨立出來了這個類。

<?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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM