How to run process in background in laravel?

Before we learn about supervior we need to learn about laravel background jobs. Basically if you are working on large scale applications and you care about website performance you may run time consuming tasks in the background.

For example, if you have a web page where your user uploads a video however video processing tasks takes longer then usual and you do not want your user to wait until it is done rather you want them to be notified when video uploading is done.

Such tasks can be run in the background by deteching the web request. Let's learn how to achieve such tasks using laravel framework.

How to setup laravel queue with database?

In order to run background jobs using database driver you need to have few tables. Run following command to add migration that creates few tables related to our background jobs.

# add migration tables
$ php artisan queue:table

# run the migration
$ php artisan migrate

How to setup supervisor?

Once you have queue driver setup how does laravel know when to run these background jobs? Well, laravel needs to know about this jobs.

We have to setup a daemon that runs in background and keep looking for active jobs and process them as it finds a new job queued in the database.

Supervisor is basically linux based daemon that runs in the background. We have to configure our laravel app so that supervisor know about background jobs.

To install and configure supervisor we will run following commands in our linux terminal window:

# install supervisor
$ sudo apt-get install supervisor

# go to supervisor config directory
cd /etc/supervisor/conf.d

# create a new laravel configuration
$ sudo nano laravel-worker.conf

# paste following contents to our file
# save changes using Ctrl + X + Y + Enter
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app.com/artisan queue:work sqs --sleep=3 --tries=3 --daemon
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/var/www/app.com/worker.log

Make sure in above file to change the location of your project directory by replacing  /var/www/app.com and to replace user with your linux logged in user id.

Once we saved the contents we need to run following command to initialize new configurations:

# read the new config
$ sudo supervisorctl reread

# activate our configuration
$ sudo supervisorctl update

# start queue command
$ sudo supervisorctl start laravel-worker:*

# check the status of our new config
$ sudo supervisorctl status

That is it, we are all setup with supervisor. Our next task will be to create a new job and push that job into database. Our supervior will pick up this new job and process it in the background.

How to create queue in laravel?

Let's take a simple example, you have a blog website. You want to notify your blog subsribers when new post is created.

Sending emails to all subscribers is a time consuming tasks you dont want to wait until this process is done so that you can move on with new posts.

We will create a new background job that deals with notifying your post subscribers. To create a new job run following command on your terminal window:

# create a new job
$ php artisan make:job onPostPublished

When running above command, it will create a new file under app/Jobs/onPostPublished.php file with following contents.

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class onPostPublished implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        // define any class variables here
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // your logic for notifying subscriber goes here
    }
}

Now, that we have job class ready we need to trigger this job from our laravel controller class. In our laravel controller function where we deal with saving our post we will queue this job.

Let's say that you have a post controller and save method to save post contents. We will call our job from that method and schedule in our database.

Let's update our post controller and call our job method.

<?php

namespacenamespac  App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Save post and notify subscribers
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        // find or create new post
        $post = Post::firstOrNew($id);

        // your logic to save post goes here

        // pass post object to our job
        // our post class has info regarding new post
        $this->dispatch( (new onPostPublished($post))->onQueue('emails') );
    }
}

Let's update our job class so that it takes post and notify all subscribers:

<?php

namespace App\Jobs;

use App\Post;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class onPostPublished implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $post;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // your logic for notifying subscriber goes here
        foreach($this->post->subscribers() as $user) {
           // send them an email
        } 
    }
}

Finally, we need to tell laravel that we will use database driver. Open your .env file and add following line:

QUEUE_DRIVER=database

That is it. We successfully created queue and using database driver this queue will be placed and processed by supervisor.