剖析 Laravel 计划任务--避免重复

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

原文连接 https://divinglaravel.com/task-scheduling/preventing-overlappingios

Sometimes a scheduled job takes more time to run than what we initially expected, and this causes another instance of the job to start while the first one is not done yet, for example imagine that we run a job that generates a report every minute, after sometime when the data gets huge the report generation might take more than 1 minute so another instance of that job starts while the first is still ongoing.laravel

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

In most scenarios this is fine, but sometimes this should be prevented in order to guarantee correct data or prevent a high server resources consumption, so let's see how you can prevent such scenario in laravel:github

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

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

Laravel will check for the Console\Scheduling\Event::withoutOverlapping class property and if it's set to true it'll try to create a mutex for the job, and will only run the job if creating a mutex was possible.服务器

Laravel将检查 Console\Scheduling\Event::withoutOverlapping 类属性,若是设置为true,它将尝试为做业建立互斥,而且只有在建立互斥的状况下才能运行该做业。app

But what's a mutex?

可是上面是互斥?

Here's the most interesting explanation I could find online:ide

这是我能够在网上找到最有趣的解释:this

When I am having a big heated discussion at work, I use a rubber chicken which I keep in my desk for just such occasions. The person holding the chicken is the only person who is allowed to talk. If you don't hold the chicken you cannot speak. You can only indicate that you want the chicken and wait until you get it before you speak. Once you have finished speaking, you can hand the chicken back to the moderator who will hand it to the next person to speak. This ensures that people do not speak over each other, and also have their own space to talk. Replace Chicken with Mutex and person with thread and you basically have the concept of a mutex.

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

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

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

So Laravel creates a mutex when the job starts the very first time, and then every time the job runs it checks if the mutex exists and only runs the job if it doesn't.

因此看成业第一次启动时,Laravel建立一个互斥,而后每次做业运行时,它检查互斥是否存在,只有在没有工做的状况下运行。

Here's what happens inside the withoutOverlapping method:

这里是 withoutOverlapping 方法中作的事

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

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

So Laravel creates a filter-callback method that instructs the Schedule Manager to ignore the task if a mutex still exists, it also creates an after-callback that clears the mutex after an instance of the task is done.

所以,Laravel建立一个filter-callback方法,指示Schedule Manager忽略任务,若是互斥仍然存在,它还会建立一个在完成任务实例后清除互斥的回调。

Also before running the job, Laravel does the following check inside the Console\Scheduling\Event::run() method:

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

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

Where does the mutex property come from?

互斥体属性来自哪里?

While the instance of Console\Scheduling\Schedule is being instantiated, laravel checks if an implementation to the Console\Scheduling\Mutex interface was bound to the container, if yes it uses that instance but if not it uses an instance of Console\Scheduling\CacheMutex:

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

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

Now while the Schedule Manager is registering your events it'll pass an instance of the mutex:

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

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

By default Laravel uses a cache-based mutex, but you can override that and implement your own mutex approach & bind it to the container.

默认状况下,Laravel使用基于缓存的互斥,但您能够覆盖它并实现本身的互斥方法并将其绑定到容器。

The cache-based mutex

基于缓存的互斥

The CacheMutex class contains 3 simple methods, it uses the event mutex name as a cache key:

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

Mutex removal after task finishes

任务完成后的互斥删除

As we've seen before, the manager registers an after-callback that removes the mutex after the task is done, for a task that runs a command on the OS that might be enough to ensure that the mutex is cleared, but for a callback task the script might die while executing the callback, so to prevent that an extra fallback was added in Console\Scheduling\CallbackEvent::run():

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

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

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

若是以为本篇文章对您十分有益,何不 打赏一下

谢谢打赏

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

相关文章
相关标签/搜索