Laravel 作业不是异步的

Laravel Jobs are not asynchronous

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

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

TestOne.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

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

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

HomeController.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");
    }
}

但是 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 

我正在 运行与 php artisan queue:listen

一起工作

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

我正在使用 Laravel 5.2。同样,我使用 "database" 作为队列 driver,是的,我已经迁移了作业 table。 driver不可以使用数据库吗?

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

Queue 有点符合 FIFO(先进先出)的概念。当你去超市时,如果你的商品比第二个人多很多(处理时间太多)也没关系,如果你排在第一位,第二个人就得等你吃完。这就是队列的工作原理。

为了让你达到你想要的效果,我建议一个简单的测试练习。

  • 停止queue:listen
  • 调用控制器(使两个作业都排队);
  • 从终端调用php artisan queue:work &
  • 按向上箭头并快速再次发出命令。

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

这是我的输出

[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

我想表达的观点是:

  • 控制器不必等待作业完成(这就是异步的意思)
  • queue:listen 一次将 运行 一项工作,并且只会在第一项完成后才开始下一项工作;
  • queue:work 将开始排队的第一个作业并将其标记为已保留(第 reserved_at 列),以便下一个 queue:work 可以接受下一个未保留的作业。

在不同队列上推送作业,即 queue1、queue2 等

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

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

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

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

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

这样您不仅可以对它们进行分类,还可以优先考虑队列工作人员如何处理它们。

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

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

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

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

您还可以使用单个队列工作器,它可以优先处理队列的方式,这样您就可以为某些作业提供比其他作业更高或更低的优先级:

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

通过使用像 Supervisor 这样的进程监视器,您甚至可以在多个进程中生成一个 worker 作为 detailed in the documentation

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

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