Yii ConsoleApplication and Gearman Workers PSD to JPEG conversation

Hi. I am going to show small example of how I use gearman with my Yii applications.
If someone doesn’t know what is gearman here is quick explanation from the official website http://gearman.org

A Gearman powered application consists of three parts: a client, a worker, and a job server. The client is responsible for creating a job to be run and sending it to a job server. The job server will find a suitable worker that can run the job and forwards the job on. The worker performs the work requested by the client and sends a response to the client through the job server. Gearman provides client and worker APIs that your applications call to talk with the Gearman job server (also known as gearmand) so you don’t need to deal with networking or mapping of jobs. Internally, the gearman client and worker APIs communicate with the job server using TCP sockets.

Our example application converts psd to jpeg. We have to install gearman job server, gearman pecl extension and ImageMagick on the server.

We will use standart Yii application skeleton as playground.

First of all we gonna create new folder within our protected folder called «workers».
As our console application use separate config file. Usually called console.php place into config folder. We have import our newly create folder.

protected/config/console.php

//....
'import' => array(
        'application.models.*',
        'application.components.*',
        'application.workers.*',
),
'params'=>array(
    'exportDir' => realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'www' . DIRECTORY_SEPARATOR . 'export'),
    'exportUrl' => '/export',
),
'components'=>array(
//.......

From now all classes from workers directory will be autoloadable.
Also we defined two variables exportDir and exportUrl which will be accessable globally within our app by calling this Yii::app()->params['exportUrl'] and Yii::app()->params['exportDir'] . Please correct this path to satisfy your needs. All generated PSD previews will be stored there.

Lets create our worker class.
Here is example code of my worker which create jpg preview from the PSD file. It uses ImageMagick.

/protected/workers/Psd2JpegWorker.php

class Psd2JpegWorker extends GearmanWorker {
 
    public $job;
    private $dir;
    private $args;
 
    public function psd2jpg($job) {
        $this->job = $job;
        $workload = $this->job->workload();
        echo "Received job psd2jpg: " . $this->job->handle() . "\n";
        $this->args = CJSON::decode($workload, false);
 
        $rnd = isset($this->args->rnd) ? $this->args->rnd : '';
        $psd = isset($this->args->psd) ? $this->args->psd : '';
 
        $this->dir = Yii::app()->params['exportDir'] . DIRECTORY_SEPARATOR . $rnd;
        echo "trying to create directory " . $this->dir . "\n";
 
        if (!is_dir($this->dir) && mkdir($this->dir, 0777) && chmod($this->dir, 0777)) {
 
            echo "Directory created\n";
            $this->convert($psd, $this->dir . DIRECTORY_SEPARATOR . 'preview.jpg');
        }
    }
 
    private function convert($src, $target) {
 
        $width = isset($this->args->width) ? $this->args->width : 400;
        $height = isset($this->args->height) ? $this->args->height : 300;
 
        echo "Converting\n";
        $cmd = "convert " . $src . "[0] -thumbnail ".$width."x".$height." " . $target . "\n";
        echo $cmd;
        exec($cmd);
    }
 
}

Method psd2jpg receive GearmanJob object, Then we get required parameters as the psd location on the server and random folder name.
We create new directory. All output will be placed there.
After all preparings are done we call «convert» command (ImageMagick) with list of arguments.
Everything is quite simple.

Let’s create Console command which will register this worker on the job server and run worker.

/protected/commands/ConvertCommand.php

<!--?php 
class ConvertCommand extends CConsoleCommand {
 
    public function run($args) {
 
        $worker = new Psd2JpegWorker();
        $worker--->addServer();
        $worker->addFunction("psd2jpg", array($worker, "psd2jpg"));
 
        while (1) {
            print "Waiting for job...\n";
            $ret = $worker->work();
            if ($worker->returnCode() != GEARMAN_SUCCESS)
                break;
        }
    }
 
}

Here we registered new worker and it supports one function called «psd2jpg».

For running console command we also have to create entire script which will instatiate new ConsoleApplication

/protected/dispatch.php

<!--?php
 
require_once(dirname(__FILE__).'/../framework/yii.php');
$config=dirname(__FILE__).'/config/console.php';
 
 // remove the following line when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);
 Yii::createConsoleApplication($config)--->run();

This code very similiar to standart Yii’s index.php but it runs ConsoleApplication instead of WebApplication

Now lets run our new worker.
open console, change directory to the protected folder and run this.

php dispatch.php

Yii lookups commands directory and you may see something like this

php dispatch.php
Yii command runner (based on Yii v1.1.10)
Usage: dispatch.php  [parameters...]
 
The following commands are available:
 - convert

You have one command available — everything is ok. Run it!

php dispatch convert
php dispatch.php convert
Waiting for job...

Our worker has been started and wait for job.
Let’s give him some job.
For doing this we have to use GearmanClient.

/protected/controllers/SiteController.php

   public function actionIndex(){
 
      $psd = "/var/www/website/www/psds/some.psd";
      $rnd = md5($psd);
 
      $client = new GearmanClient();
      $client->addServer();
 
      $params = CJSON::encode(array('psd' => $psd, 'width' => 400, 'height' => 300, 'rnd' => $rnd));
      $handle = $client->do("psd2jpg", $params);
 
      $targetUrl = Yii::app()->params['exportUrl'] . DIRECTORY_SEPARATOR . $rnd . DIRECTORY_SEPARATOR . 'preview.jpg';
      $this->redirect($targetUrl);
 
   }

After psd will be converted prowser will be redirected to resulted file.
Run this action by typing %websitename&/index.php?r=site/index
Ensure that each requrest to this action initiate worker.
Play with number of instances of workers runned («php dispatch convert &», «php dispatch convert &», «php dispatch convert &»)
Try to change

$client->do("psd2jpg", $params);

to

$client->doBackground("psd2jpg", $params);

Hope this article will help someone. Have fun!
PS. I found this extension which make work with gearman even easier.