Laravel是怎樣防止你的定時任務重復執行的

Laravel是怎樣防止你的定時任務重復執行的

基本介紹

有時候一個定時任務執行需要的時間可能會比我們想象的要長,這就會引起一個問題——當前任務還沒有執行完畢的時候另一個相同的任務也會執行,從而導致任務重復。例如想象一下我們執行每分鐘生成一次報告的任務,在經過一段時間后,數據量變得很大導致執行時間多于1分鐘,這樣就會導致在上一個任務還沒結束的時候另一個相同的任務開始執行。

解決方法

大部分情況下是沒有什么問題的,但是有時我們需要避免這種情況來保證獲得正確的數據。在laravel中我們可以通過withoutOverlapping方法來進行處理:

$schedule->command('mail:send')->withoutOverlapping();

Laravel會檢查ConsoleSchedulingEvent::withoutOverlapping屬性,如果該值為true那么將會針對這個任務創建一個互斥鎖(mutex),并且只有在可以創建互斥鎖的情況下才會執行此任務。

什么是互斥鎖?

這是我在網上找到的最有趣的解釋:

當我們在開會進行激烈的討論時,我會從我桌子里拿出來一個尖叫雞。只有手里拿著尖叫雞的人才能說話,如果你沒有拿著尖叫雞你是不能說話的。你只能向會議主持人請示,只有在你拿到尖叫雞的時候你才能說話否則只能等待。當你講話完畢的時候,將尖叫雞還給會議主持人,主持人會將尖叫雞給到下一個人來讓其說話。這樣會確保人們不會互相交談,同時他們也會有自己的時間來進行講話。

將尖叫雞換成互斥鎖,人換成線程。你基本上就有了一個互斥鎖的基本概念。

https://stackoverflow.com/questions/34524/…

原理分析

Laravel在第一次執行任務的時候會創建一個互斥鎖,然后在每次執行任務時會檢查互斥鎖是否存在,只有互斥鎖不存在的時候任務才會執行。下面是withoutOverlapping方法:

public function withoutOverlapping() {     $this->withoutOverlapping = true;      return $this->then(function () {         $this->mutex->forget($this);     })->skip(function () {         return $this->mutex->exists($this);     }); }

Laravel創建了一個過濾回調方法來告訴計劃管理器忽略互斥鎖仍然存在的任務,同時也創建了一個在完成任務實例后清除互斥鎖的回調。同時,在執行任務之前,Lravel會在ConsoleSchedulingEvent::run()方法中依次執行下面一系列的檢查:

if ($this->withoutOverlapping && ! $this->mutex->create($this)) {     return; }

那么互斥鎖的屬性是從哪里來的呢?

當ConsoleSchedulingSchedule被實例化的時候,Laravel會檢查ConsoleSchedulingMutex是否綁定到了容器,如果是那么就會實例化它,否則會使用ConsoleSchedulingCacheMutex

$this->mutex = $container->bound(Mutex::class)                         ? $container->make(Mutex::class)                         : $container->make(CacheMutex::class);

現在當任務管理器在注冊事件的時候會將互斥鎖的實例一并傳進去:

$this->events[] = new Event($this->mutex, $command);

Laravel默認使用了緩存實現的互斥鎖,但是你可以自己實現并替換它。

緩存版的互斥鎖

CacheMutex類只有3個簡單的方法,它使用了事件互斥鎖的名字作為緩存的鍵值:

public function create(Event $event) {     return $this->cache->add($event->mutexName(), true, 1440); }  public function exists(Event $event) {     return $this->cache->has($event->mutexName()); }  public function forget(Event $event) {     $this->cache->forget($event->mutexName()); }

就像我們之前看過的,管理器注冊了一個執行后回調來保證任務執行完畢的時候移除互斥鎖,對于一個系統里的命令來說也許已經可以確保移除了。但是對于一個回調方法的任務來說腳本可能在執行回調的時候結束,因此為了避免這種情況在ConsoleSchedulingCallbackEvent::run()方法中加入了下面的代碼確?;コ怄i在任務意外關閉的時候能夠正常移除:

register_shutdown_function(function () {     $this->removeMutex(); });

更多Laravel相關技術文章,請訪問https://stackoverflow.com/questions/34524/…欄目進行學習!

? 版權聲明
THE END
喜歡就支持一下吧
點贊9 分享