In general programming we often come across situations where our api/page has to perform a lengthy task, and which not matches the standard response time and for that the entire application gets slow down. Recently I faced the same when I have to calculate best resolution for 100 video content and pass them for encoding and the standard api time was 1 second.

Initially, api response time was 32 seconds but after running the process in background its minimised to 43ms. Yii2 provides an advance feature to run bulk task using console controller. Console controller works with in console environment. Here I found a way to do it from code level with object as a parameter.

Approach

  1. Create  a Console Controller. Define a method. You can have all your background processing task within the same controller with different different methods.
  2. Call the method with shell_exec.
  3. To pass an object as a parameter, convert your object to json string and pass as a single string.
  4. On the Console Controller again decode it to get the original object.
  5. Actually we can not pass an direct object to a console controller. Because it accepts parameters in following manner only as its developed to run in a console only.
    php yii <contriller name>/<function name> argument1 argument2

Create a Console Controller

In BulkVideo action, you can see I have decoded the $input json string.

<?php
 namespace app\controllers;
 use Yii;
 use yii\console\Controller;
 class BackgroundController extends Controller
 {
     public function actionIndex()
     {
         echo "I am executed in a Console";
     }
     
     public function actionBulkvideo($input)
     {
         $data = json_decode($input,true);
         Yii::$app->bulkVideoProcessor->videoData($data);
     }
 }

Setup Config for Console Controllers

Console controllers refers configurations from config/console.php. Its similar to config/web.php. You need to add required components to be used in console, path for db and params etc. See the below example,

<?php
 $params = require DIR . '/params.php';
 $db = require DIR . '/db.php';

 $config = [
     'id' => 'basic-console',
     'basePath' => dirname(DIR),
     'bootstrap' => ['log'],
     'controllerNamespace' => 'app\commands',
     'aliases' => [
         '@bower' => '@vendor/bower-asset',
         '@npm'   => '@vendor/npm-asset',
         '@tests' => '@app/tests',
     ],
     'components' => [
         'cache' => [
             'class' => 'yii\caching\FileCache',
         ],
         'log' => [
             'targets' => [
                 [
                     'class' => 'yii\log\FileTarget',
                     'levels' => ['error', 'warning'],
                 ],
             ],
         ],
         'db' => $db,
         'bulkVideoProcessor' => ['class' => 'app\components\BulkVideoProcessor',]
     ],
     'params' => $params,
     'controllerMap' => [
         'background' => [ 
             'class' => 'app\controllers\BackgroundController',
         ],
     ],
 ];
 if (YII_ENV_DEV) {
     // configuration adjustments for 'dev' environment
     $config['bootstrap'][] = 'gii';
     $config['modules']['gii'] = [
         'class' => 'yii\gii\Module',
     ];
 }
 return $config;

Calling this background controller from another controller

From any part of code we can call this. Here the input string is $data. We have used shell command and pass its output to /dev/null 2>&1 &, which will not wait for entire process to be completed and jump to execute next line. Note: In some cases you may require to use escapeshellarg() for the $jsonData argument.

$jsonData = json_encode($data);
$shellCommand = "php ".Yii::getAlias('@app')."/yii background/bulkvideo '".$jsonData."'> /dev/null 2>&1 &";
shell_exec($shellCommand);
echo "Task is running in background"; // You can return your API response here.

Thanks for reading! If you have any doubt regarding this feel free to add in the comment.

Leave a Reply

Your email address will not be published. Required fields are marked *