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.
I created to test jobs using the command line: 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);
}
}
I simply log some messages in laravel's log file, and since TestOne is sleeping for 5 seconds, TestTwo should log the messages first
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");
}
}
However TestTwo job still waits until TestOne job is done:
[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
What am I doing wrong here? I really need these tasks to run asynchronously, just like, say, a JS AJAX request would work like.
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?
php - Laravel Jobs are not asynchronous - Stack Overflow. Stack Overflow for Teams – Start collaborating and sharing organizational knowledge.
An asynchronous request is a request that is executed in the background, so you don't have to wait for the response to continue your code. The Laravel HTTP Client has the async() method for that.
Laravel supports asynchronous events by allowing Listeners (which are classes) to implement a ShouldQueue marker interface to indicate that if a queue system is available the Listener should not execute immediately but a queue entry should be made that will, in turn, execute the Listener "later".
By default, Laravel sets the value of this variable to sync , which means that the framework will process all the jobs synchronously.
To process jobs in parallel you'd have to split them across different queues as @dparoli pointed out.
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.
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.
This holds true for all queue drivers.
Asynchronous mean 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 Synchronous execution, it would take 15 seconds for die
to be reached.
Queue kind 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.
For you to achieve what you want, I suggest a simple test exercise.
queue:listen
php artisan queue:work &
from the terminalSince &
will send the process to the background, you'll be free to issue queue:work
twice almost instantaneous. This should bring the behavior you expect.
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:
queue:listen
will run one job at a time and will only start the next after the 1st finished;queue:work
will start the 1st job in line and mark it as reserved (column reserved_at
) so the next queue:work
can take the next job not reserved.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With