Laravel Jobs不是异步的

时间:2017-03-04 17:31:36

标签: php laravel asynchronous jobs

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

我创建了使用命令行测试作业: php artisan make:job TestOne 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。我再次使用&#34;数据库&#34;作为队列驱动程序,是的,我已经迁移了jobs表。是否无法将数据库用作驱动程序?

3 个答案:

答案 0 :(得分:5)

要并行处理作业,你必须像@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这样的流程监视器,您甚至可以在detailed in the documentation的多个流程中生成单个工作人员。

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

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

答案 1 :(得分:3)

异步表示作业不会执行控制器方法。例如,如果您将sleep(5);添加到One,sleep(10);添加到2,die('stop');仍会在您请求控制器时立即发生。在同步执行中,到达die需要15秒。

队列类似FIFO的概念(先进先出)。当你去超市时,如果你有比第二个人更多的物品(处理时间太长)并不重要,如果你是第一个在线,第二个将不得不等你完成。这就是Queue的工作方式。

为了达到你想要的目标,我建议进行简单的测试练习。

  • 停止queue:listen
  • 调用Controller(导致两个作业都排队);
  • 从终端
  • 拨打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可以接受下一个未保留的作业。

答案 2 :(得分:2)

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

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

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

您可以根据文档使用supervisord监视队列工作程序。

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