【注意】最后更新于 March 5, 2020,文中内容可能已过时,请谨慎使用。
问题
一个Task
没有允许结束,另一个Task
又起了,这固然会造成程序执行的结果不是我们想要的,为了解决这个问题我发现Laravel
有提供现成的方法。
方案
方法一:可以在代码中使用原子锁,这样即使第二个tash
进程启动了,也可以阻挡住。
方法二:使用Laravel
提供现成的withoutOverlapping
方法,官方案例:$schedule->command('emails:send')->withoutOverlapping();
方法一中使用哪一种的锁机制,那就看自家自己的喜好了不分好坏,只分性能高低😼,个人使用redis
原子锁,从2.6.12
版本以后,使用锁的方式变得更为简单,如设置一个名为lock
的5秒钟原子锁:SET lock 1 EX 5 NX
,SET 更多参数用法
方法二我的测试结果如下:
创建测试command
文件,命令:php artisan make:command Test
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| class Test extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test:over';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->line('Start');
\Log::info('command run time:'.date('Y-m-d H:i:s'));
sleep(90);
$this->line('End');
}
}
|
Kernel.php
代码
1
2
3
4
5
6
7
8
9
10
11
12
| protected $commands = [
...
...
Commands\Test::class
]
protected function schedule(Schedule $schedule)
{
...
...
$schedule->command('test:over')->everyMinute()->withoutOverlapping();
}
|
- 测试结果
1
2
3
4
5
6
7
8
9
| [2020-03-09 13:00:02] production.INFO: command run time:2020-03-09 13:00:02
[2020-03-09 13:02:00] production.INFO: command run time:2020-03-09 13:02:00
[2020-03-09 13:04:02] production.INFO: command run time:2020-03-09 13:04:02
[2020-03-09 13:06:01] production.INFO: command run time:2020-03-09 13:06:01
[2020-03-09 13:08:02] production.INFO: command run time:2020-03-09 13:08:02
[2020-03-09 13:10:01] production.INFO: command run time:2020-03-09 13:10:01
[2020-03-09 13:12:02] production.INFO: command run time:2020-03-09 13:12:02
[2020-03-09 13:14:01] production.INFO: command run time:2020-03-09 13:14:01
[2020-03-09 13:16:02] production.INFO: command run time:2020-03-09 13:16:02
|
只要不人为干预强制启动多个schedule:run
,不会有问题。
在19:32秒时强制,人为强制启动多个schedule:run
进程,log如下:
1
2
3
4
5
6
| [2020-03-09 13:18:01] production.INFO: command run time:2020-03-09 13:18:01
[2020-03-09 13:19:32] production.INFO: command run time:2020-03-09 13:19:32
[2020-03-09 13:22:02] production.INFO: command run time:2020-03-09 13:22:02
[2020-03-09 13:24:01] production.INFO: command run time:2020-03-09 13:24:01
[2020-03-09 13:26:02] production.INFO: command run time:2020-03-09 13:26:02
[2020-03-09 13:28:01] production.INFO: command run time:2020-03-09 13:28:01
|
原理分析
1
2
3
4
5
6
7
8
9
10
11
12
13
| /**
* Do not allow the event to overlap each other.
*
* @return $this
*/
public function withoutOverlapping()
{
$this->withoutOverlapping = true;
return $this->skip(function () {
return file_exists($this->mutexPath());
});
}
|
1
2
3
4
5
6
7
8
9
10
11
12
| /**
* Register a callback to further filter the schedule.
*
* @param \Closure $callback
* @return $this
*/
public function skip(Closure $callback)
{
$this->rejects[] = $callback;
return $this;
}
|
1
2
3
4
5
6
7
8
9
| /**
* Get the mutex path for the scheduled command.
*
* @return string
*/
protected function mutexPath()
{
return storage_path('framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command));
}
|
1
2
3
| ➜ ls storage/framework
cache schedule-7d2b691b1562e375e4f787cfd391d7a019e5f8cf sessions views
➜ ✗ cat storage/framework/schedule-7d2b691b1562e375e4f787cfd391d7a019e5f8cf
|
参考