Concurrency in PHP with Swoole

Swoole is a CLI server written in C as a PHP extension. It handles HTTP, TCP and socket servers, offering some really interesting features. The ones I’ve tested and I’m pretty excited about are related to concurrency.

The authors have implemented a concurrency model similar to Go‘s, using coroutines and channels for inter-process communication. It also has a memory map, atomic objects, locks, buffers, timers and others.

I’ve tested their 2.2 version found in PECL, but very recently they’ve release the 4.0.0 version. My enthusiasm suffered a little bit when they’ve introduced a pretty nasty bug with 4.0.0, but they were fast about fixing it, although a new version is not released at the time of writing (but you can compile from master branch).
It’s funny that one day everything was working as expected, and the other day I spent some time until I’ve figured they had just released a new version, and that was causing my issue. I had installed the extension without specifying the version, so a new install got me the latest one.

Below is a very small example of concurrency using routines and a channel.

First, the traditional way of iterating some data and calling a validation function. If the function slowValidationFunction takes 1 seconds, for 60 items to validate the entire process takes 1 minute.

$items = ["item1", "item2", "item3" ...];
$size = count($items);
$chan = new chan($size);

$result = [];
foreach ($items as $item) {
    $valid = slowValidationFunction($item);
    $result[$item] = $valid;
}

print_r($result);

Making the calls to slowValidationFunction in parallel, everything takes 1 second (the limit for this method is the hardware resources – RAM, and CPU in some cases).

$items = ["item1", "item2", "item3" ...];
$size = count($items);
$chan = new chan($size);

foreach ($items as $item) {
    go(function () use($item, $chan) {
        $valid = slowValidationFunction($item);
        $chan->push([$item => $valid]);
    });
}

$result = [];
for ($i = $size; $i > 0; $i--) {
    $result = array_merge($result, $chan->pop());
}

If you’re familiar to Go, there’s no secret, otherwise the idea is:

  • You create a channel $chan which holds a given number $size of items. What’s special about this channel is that it’s safe to use between different PHP processes. Unlike usually, we are not going to execute the entire code in one process, instead we’ll have a main process and a number of $size extra processes, one for every call of slowValidationFunction() that we’ll run in parallel.
  • Using the Swoole go function with a function as argument, we start $size processes with the item validation. We push the result of each validation into the channel.
  • Finally, we read everything from the channel and go further.

With coroutines you can introduce subtle issues that are sometimes hard to manually debug. The authors are currently working on a debugging tool for coroutines, as this can’t be done with Xdebug.

Swoole is very easy to install from PECL:

pecl install swoole-2.2.0

If you want to test it yourself, here’s a full environment and a more relevant application: https://github.com/andreiavrammsd/php-concurrent-image-resize-service. Documentation can be found on their website and on php.net.

I hope this gets really big into PHP and takes it to another level!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.