译文GitHub https://github.com/yuansir/diving-laravel-zh

原文链接 https://divinglaravel.com/task-scheduling/preventing-overlapping

有时一个预定的工作需要比我们最初预期的更多的时间运行,这样会导致另外一个工作的实例开始,而第一个还没有完成,例如,我们运行一个每分钟生成报告的工作有时候当数据变大时,报表生成可能需要1分钟以上,这样就可以在第一个还在进行时启动该作业的另一个实例。

在大多数情况下,这是很好的,但有时候应该防止这种情况,以保证正确的数据或防止高的服务器资源消耗,所以让我们看看如何防止这种情况在laravel中发生:

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

Laravel将检查 Console\Scheduling\Event::withoutOverlapping 类属性,如果设置为true,它将尝试为作业创建互斥,并且只有在创建互斥的情况下才能运行该作业。

但是上面是互斥?

这是我可以在网上找到最有趣的解释:

当我在工作中进行热烈的讨论时,我使用一只橡胶鸡,我在这样的场合放在桌子上。 持有鸡的人是唯一被允许谈话的人。 如果你不握鸡,你不会说话。 你只能指示你想要鸡,等到你说话之前才能得到它。 一旦你完成演讲,你可以将鸡回到主持人,他将把它交给下一个人说话。 这样可以确保人们互不说话,也有自己的空间。 用线替换鸡与互斥和人,你基本上有一个互斥的概念。

-- https://stackoverflow.com/questions/34524/what-is-a-mutex/34558#34558

所以当作业第一次启动时,Laravel创建一个互斥,然后每次作业运行时,它检查互斥是否存在,只有在没有工作的情况下运行。

这里是 withoutOverlapping 方法中做的事

public function withoutOverlapping()  
{
    $this->withoutOverlapping = true;

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

因此,Laravel创建一个filter-callback方法,指示Schedule Manager忽略任务,如果互斥仍然存在,它还会创建一个在完成任务实例后清除互斥的回调。

在运行该作业之前,Laravel会在Console\Scheduling\Event::run()方法中进行以下检查:

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

互斥体属性来自哪里?

Console\Scheduling\Schedule 的实例被实例化时,laravel会检查 Console\Scheduling\Mutex 接口的实现是否绑定到容器,如果是,则使用该实例,如果不是,使用Console\Scheduling\CacheMutex实例:

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

现在,Schedule Manager正在注册你的事件,它会传递互斥的一个实例:

$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());
}

任务完成后的互斥删除

如前所述,管理器注册一个在完成任务之后删除互斥的回调,对于在操作系统上运行命令的任务可能足以确保互斥被清除,但是对于回调 执行回调时脚本可能会死机,所以为了防止这种情况在 Console\Scheduling\CallbackEvent::run()中添加了一个额外的回退:

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

转载请注明: 转载自Ryan是菜鸟 | LNMP技术栈笔记

如果觉得本篇文章对您十分有益,何不 打赏一下

本文链接地址: 剖析Laravel计划任务--避免重复