利用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命令來執行出隊操作,然後進行任務的執行。方法調用如下:
- artisan queue:work
- WorkerCommand:fire()
- Worker:pop()
- Worker:getNextJob()
- RedisQueue:pop()
- 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
-
}
-
}
即先刪除這個任務,再開始執行任務。