Close search
Hoa

Hack book of Hoa\Worker

Workers free an application of heavy or blocking tasks. The Hoa\Worker library creates and manipulates workers effortlessly.

Table of contents

  1. Introduction
  2. Shared workers
    1. Where are the wid profiles stored?
  3. Communicate with a worker
  4. Use dedicated commands
  5. Conclusion

Introduction

It is advisable to read the Hoa\Zombie chapter to learn how to transform a processus into a zombie. This is the idea behind Hoa\Worker to setup a server that will receive messages after the zombie creation. For each received message, an event will be issued so that we intercept to execute a task.

These tasks can be of several kinds such as sending a notification (e.g. by email), doing important operations on database, handling many files etc. The idea is to unload the application from tasks that could slow it down. These tasks may be run in parallel and must not interfere with the execution of the application.

Although the established protocol allows some integrity and security check for messages, it is preferable to do communication from server to server and not through the client.

Hoa\Worker is made of two parts:

Shared workers

A worker is primarily identified by a workerId, sometimes abbreviated wid. This identifier is used to create a profile for our worker thanks to Hoa\Worker\Run that does only this. Only an identifier and the server address created in the worker are required to establish a profile. Then, we can start a worker based on its identifier.

The first step will be to check that the profile exists through the Hoa\Worker\Run::widExists method. On the other hand, we will create it with the Hoa\Worker\Run::register method. Thus:

if (false === Hoa\Worker\Run::widExists('demorker')) {
    Hoa\Worker\Run::register('demorker', 'tcp://127.0.0.1:123456');
}

We are now sure that the profile exists. We can then start our worker. For this, we must use its identifier and its password (required to stop it) in the constructor of Hoa\Worker\Backend\Shared. After creating the object, it will broadcast only one category of event: message, that we will listen to establish our own task processing protocol. Finally the Hoa\Worker\Backend\Shared::run starts the worker:

$file   = new Hoa\File\Write(__DIR__ . DS . 'Log');
$worker = new Hoa\Worker\Backend\Shared('demorker', 'iamapassword');
$worker->on('message', function (Hoa\Event\Bucket $bucket) use ($file) {
    // compute messages.
    $data = $bucket->getData();
    $file->writeAll($data['message'] . "\n");
});
$worker->run();

We are free to do what we want to process the task! The format is completely free: Binary, compressed, command line, serialized object… we can send whatever we want.

Where are the wid profiles stored?

The worker profiles are stored in the hoa://Data/Variable/Run/workerId.wid files. Pay attention to verify that this path is well defined, for example by choosing the folder /path/to/wid/directory:

$protocol = Hoa\Protocol\Protocol::getInstance();
$protocol['Data']['Variable']['Run']->setReach("\r" .  '/path/to/wid/directory);
var_dump(resolve('hoa://Data/Variable/Run'));

/**
 * Will output:
 *     string(21) "/path/to/wid/directory
 */

Note that if we use Hoa with its Data/ folder, then hoa://Data/ will be defined automatically to this folder and we will not need to redefine the protocol. In all cases, it will not be necessary to modify the code but only the hoa:// protocol.

Communicate with a worker

A worker stands for a server, so we will introduce its client allowing to send tasks to it. The client is very easy to use, simply instantiate the Hoa\Worker\Shared class by specifying the worker identifier, then use the proper method to send messages, namely Hoa\Worker\Shared::postMessage:

$worker = new Hoa\Worker\Shared('demorker');
$worker->postMessage('mail gordon@freeman.hl Hello Gordon!');

Of course, the client will seek the worker profile in the hoa://Data/Variable/Run/workerId.wid file. If we have redefined it in the worker, we will also require to redefine it in the client.

We can use the Hoa\Worker\Shared::postMessage method as many times as we wish but beware, it connects and disconnects each time to the worker to not disturb the execution of our main application. We must then consider sending the maximum of information every time (e.g. through an array, the data are serialized in all cases).

Use dedicated commands

To execute a worker we first need to start PHP FPM:

$ php-fpm -d listen=127.0.0.1:9000

Then, to start our worker, we will use Hoa's command line and more precisely the worker:start command. It is only a convenient alias to Hoa\Worker\Backend\Shared::start needing the PHP FPM address (by default 127.0.0.1:9000) and the path to the worker file:

$ hoa worker:start /path/to/your/worker.php

To check that our worker has been created, we can use the worker:status command that will return the list of all the workers as well as information and statistics like the identifiers, server addresses, age, current memory consumption, average and maximum etc. In reality, it is only an alias to the Hoa\Worker\Shared::getInformation method. Thus:

$ hoa worker:status --run /path/to/wid/directory/
Shared worker information

ID        PID   Socket                  Uptime      Messages  Last
demorker  2465  tcp://127.0.0.1:123456  0d00:03:15  0         -

demorker  ||||||||||||||||||||||||||||||||||||   |  1,398Kb 1,398Kb 1,536Kb

1 shared worker is running.

Note: The color code does not appear in the example.

Our worker is now in place. We can run the client without problem (in command line, via a browser, whatever!).

Finally, to properly stop the worker, we have the worker:stop command, that is only an alias to Hoa\Worker\Backend\Shared::stop, requiring worker identifier and its password:

$ hoa worker:stop --run /path/to/wid/directory/ demorker
Password: ▋

If the password is correct, the worker will stop and the client won't be able to communicate with it and will throw a Hoa\Worker\Exception exception.

Conclusion

Hoa\Worker is a demonstration of what it is possible to build with Hoa\Zombie but not only. It has done its job i.e. relieving the application from heavy and blocking tasks. The worker:* commands help administrate these workers with less efforts. Nothing prevents you from building your own solution from the concepts discussed here!

An error or a suggestion about the documentation? Contributions are welcome!

Comments

menu