Laravel 作业不是异步的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42599055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 15:28:18  来源:igfitidea点击:

Laravel Jobs are not asynchronous

phplaravelasynchronousjobs

提问by mike.void

I need a way to run some tasks asynchronously as the execution time varies between each task and I want to run the in an asynchronous way using Laravel Jobs and database as the driver.

我需要一种异步运行某些任务的方法,因为每个任务之间的执行时间不同,我想使用 Laravel 作业和数据库作为驱动程序以异步方式运行。

I created to test jobs using the command line: php artisan make:job TestOne php artisan make:job TestTwo

我创建使用命令行来测试作业: php artisan make:job TestOne php artisan make:job TestTwo

TestOne.php

测试一.php

<?php

namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class TestOne extends Job implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        sleep(5);
        foreach (range(1, 10) as $item)
            \Log::info("TestOne: item #" . $item);
    }
}

TestTwo.php

测试二.php

<?php

namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class TestTwo extends Job implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        foreach (range(1, 10) as $item)
            \Log::info("TestTwo: item #" . $item);
    }
}

I simply log some messages in laravel's log file, and since TestOne is sleeping for 5 seconds, TestTwo should log the messages first

我只是在 laravel 的日志文件中记录了一些消息,由于 TestOne 休眠了 5 秒,TestTwo 应该首先记录这些消息

HomeController.php

家庭控制器.php

<?php

namespace App\Http\Controllers;

use Queue;
use App\Jobs\TestOne;
use App\Jobs\TestTwo;

class HomeController extends Controller
{
    public function index()
    {
        $this->dispatch(new TestOne());
        $this->dispatch(new TestTwo());
        die("stop");
    }
}

However TestTwo job still waits until TestOne job is done:

但是 TestTwo 作业仍然要等到 TestOne 作业完成:

[2017-03-04 17:00:30] local.INFO: TestOne: item #1  
[2017-03-04 17:00:30] local.INFO: TestOne: item #2  
[2017-03-04 17:00:30] local.INFO: TestOne: item #3  
[2017-03-04 17:00:30] local.INFO: TestOne: item #4  
[2017-03-04 17:00:30] local.INFO: TestOne: item #5  
[2017-03-04 17:00:30] local.INFO: TestOne: item #6  
[2017-03-04 17:00:30] local.INFO: TestOne: item #7  
[2017-03-04 17:00:30] local.INFO: TestOne: item #8  
[2017-03-04 17:00:30] local.INFO: TestOne: item #9  
[2017-03-04 17:00:30] local.INFO: TestOne: item #10  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #1  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #2  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #3  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #4  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #5  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #6  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #7  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #8  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #9  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #10 

I am running the jobs with php artisan queue:listen

我正在运行这些工作 php artisan queue:listen

What am I doing wrong here? I really need these tasks to run asynchronously, just like, say, a JS AJAX request would work like.

我在这里做错了什么?我真的需要这些任务异步运行,就像 JS AJAX 请求一样。

I am using Laravel 5.2. Again, I am using "database" as the queue driver and yes I have migrated the jobs table. Is it not possible using the database as the driver?

我正在使用 Laravel 5.2。同样,我使用“数据库”作为队列驱动程序,是的,我已经迁移了作业表。不能用数据库作为驱动吗?

回答by Paulo Freitas

To process jobs in parallel you'd have to split them across different queues as @dparoli pointed out.

如@dparoli 指出的那样,要并行处理作业,您必须将它们拆分到不同的队列中。

This way you can not only categorize them but also priorize how they'll be processed by your queue workers.

通过这种方式,您不仅可以对它们进行分类,还可以优先处理队列工作人员将如何处理它们。

When dispatching a job, you'll specify which queue it belongs:

分派作业时,您将指定它属于哪个队列:

$this->dispatch((new TestOne())->onQueue('queue1'));
$this->dispatch((new TestTwo())->onQueue('queue2'));

That way you can spawn multiple queue workers to process jobs separately:

这样你就可以产生多个队列工作器来分别处理作业:

php artisan queue:work --queue=queue1
php artisan queue:work --queue=queue2

You can also use a single queue worker which priorize how queues are processed, so you can give a higher or lower precedence for some jobs over others:

您还可以使用单个队列工作器来优先处理队列的处理方式,因此您可以为某些作业提供更高或更低的优先级:

php artisan queue:work --queue=queue2,queue1

By using a process monitor like Supervisor you can even spawn a single worker in multiple processes as detailed in the documentation.

通过使用像 Supervisor 这样的进程监视器,您甚至可以按照文档中的详细说明在多个进程中生成一个工人。

It's worth noting that a single queue worker which prioritizes its queues will still process their jobs by taking the FIFO precedence in addition to the given queue priority. To achieve a better parallelism you'll want to spawn multiple queue workers.

值得注意的是,除了给定的队列优先级之外,对其队列进行优先级排序的单个队列工作器仍将通过采用 FIFO 优先级来处理其作业。为了实现更好的并行性,您需要生成多个队列工作器。

This holds true for all queue drivers.

这适用于所有队列驱动程序。

回答by Marco Aurélio Deleu

Asynchronousmean that the jobs won't hold the execution of the controller method. For instance, if you add sleep(5);to One and sleep(10);to Two, die('stop');will still happens instantaneously when you request your controller. In a Synchronousexecution, it would take 15 seconds for dieto be reached.

异步意味着作业不会持有控制器方法的执行。例如,如果您添加sleep(5);到一和sleep(10);到二,die('stop');当您请求控制器时仍然会立即发生。在同步执行中,需要 15 秒die才能到达。

Queuekind of holds of the notion of FIFO (first in, first out). When you go to the Supermarket, it doesn't matter if you have a lot more items (too much processing time) than the 2nd person, if you're the first in line, the 2nd will have to wait you to finish. That's how Queue works.

FIFO 概念的队列类型(先进先出)。当你去超市时,如果你的物品比第二个人多很多(处理时间太长)也没关系,如果你排在第一个,第二个人就得等你完成。这就是队列的工作方式。

For you to achieve what you want, I suggest a simple test exercise.

为了您实现您想要的,我建议您进行一个简单的测试练习。

  • Stop the queue:listen
  • Call the Controller (causing both jobs to be queued);
  • Call php artisan queue:work &from the terminal
  • Press up arrow and issue the command again real fast.
  • 停止 queue:listen
  • 调用控制器(使两个作业都排队);
  • php artisan queue:work &从终端呼叫
  • 按向上箭头并再次快速发出命令。

Since &will send the process to the background, you'll be free to issue queue:worktwice almost instantaneous. This should bring the behavior you expect.

由于&会将进程发送到后台,您queue:work几乎可以在瞬间自由发出两次。这应该会带来您期望的行为。

This was my output

这是我的输出

[03:01 PM]-[root@php7]-[/var/www/html/jobs]
php artisan queue:work &
[1] 2456

[03:02 PM]-[root@php7]-[/var/www/html/jobs]
php artisan queue:work &
[2] 2458

[03:02 PM]-[root@php7]-[/var/www/html/jobs]
[2017-03-04 18:02:33] Processed: App\Jobs\TaskTwo
[2017-03-04 18:02:37] Processed: App\Jobs\TaskOne

The point I'm trying to make is:

我试图提出的观点是:

  • Controller will not have to wait the jobs to finish (this is what Asynchronous means)
  • queue:listenwill run one job at a time and will only start the next after the 1st finished;
  • queue:workwill start the 1st job in line and mark it as reserved (column reserved_at) so the next queue:workcan take the next job not reserved.
  • 控制器不必等待作业完成(这就是异步的意思)
  • queue:listen将一次运行一个作业,并在第一个完成后才开始下一个;
  • queue:work将开始第一个作业并将其标记为保留(列reserved_at),以便下一个queue:work可以执行下一个未保留的作业。

回答by dparoli

Push jobs on different queues, i.e. queue1, queue2 etc.

将作业推送到不同的队列,即 queue1、queue2 等。

For each queue you have defined you should have a worker:

对于您定义的每个队列,您应该有一个工作人员:

php artisan queue:work --queue=queue1
php artisan queue:work --queue=queue2

You can use supervisord to monitor queue workers as per documentation.

您可以根据文档使用 supervisord 来监控队列工作人员。

With this solution each queue run async respect others queues, but two jobs on the same queue are not async, they respect FIFO precedence.

使用此解决方案,每个队列异步运行尊重其他队列,但同一队列上的两个作业不是异步的,它们尊重 FIFO 优先级。