Not so long time ago we posted about ReactPHP and the application we made using it. We were so pleased working with ReactPHP that for some time we thought that we found the best solution for us so far. But the last thing one can say about our job as developers is that we lack dynamics. Our field is frenzy and ever changing and in order to thrive we need to observe and try everything that’s new and promising. And sometimes it turns out we found a gem. Soon after we finished our project with ReactPHP we came across something new and even faster than ReactPHP – Swoole. 

What is the reason?

Not so long time ago we posted about ReactPHP and the application we made using it. We were so pleased working with ReactPHP that for some time we thought that we found the best solution for us so far.

But the last thing one can say about our job as developers is that we lack dynamics. Our field is frenzy and ever changing and in order to thrive we need to observe and try everything that’s new and promising. And sometimes it turns out we found a gem. Soon after we finished our project with ReactPHP we came across something new and even faster than ReactPHP – Swoole.  

Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C and C++ for PHP. And below is the list of its functionalities and advantages as given by the Swoole team themselves:

    • Purely C-compiled, with extremely powerful performance;
    • Simple and easy to use, development-efficient;
    • Event-driven, non-blocking asynchronous processing;
    • Supporting millions of concurrent TCP connections;
    • TCP/UDP/UnixSock;
    • Server/client;
    • Supporting asynchronous/synchronous/coroutine;
    • Supporting multiprocessing/multi-threading;
    • CPU affinity/daemon process;
    • Supporting IPv4/IPv6 networks.

At the beginning I thought that Swoole was just like ReactPHP, but coded as an extension. After I looked deeper I realized that the work the Swoole team did was much more than I had imagined. It wasn’t only the asynchronous loop – the engine had and supported almost everything one needed to build the fastest application! An officially provided PHP network framework, extended and developed based on Swoole, supports HTTP, FastCGI, WebSocket, FTP, SMTP, RPC and other network protocols. The framework manages to make the process of many master and worker processes just as seamless as a few lines of callback code.

How to install it

You can install Swoole in a few different ways – by compiling it and as a PECL package. Of course you need PHP 7.0.0 or a later version installed on the server (the higher the version, the better the performance).

apt install php7.0-dev

1. Installation

a. Compile

git clone https://github.com/swoole/swoole-src.git
cd swoole-src
phpize
./configure
make && make install

Don’t forget to add extension=swoole.so to your php.ini.

b. PECL

pecl install swoole

2. Configuration

As mentioned above after compiling the package and installing it to the system, you have to add a new line extension=swoole.so to php.ini to enable the Swoole extension.

echo "extension=swoole.so" /etc/php/7.0/mods-available/swoole.ini
ln -s /etc/php/7.0/mods-available/swoole.ini /etc/php/7.0/cli/conf.d/10-swoole.ini

3. Testing

Okay, we have Swoole installed on our system! Let’s write our first Hello world code and test it to see if it’s working correctly.

cd /tmp/
cat < swoole.php
    \$http = new swoole_http_server('127.0.0.1', 9501);
    \$http->on('start', function (\$server) {
        echo "Start swoole server";
    });
    \$http->on("request", function (\$request, \$response) {
        \$response->header("Content-Type", "text/plain");
        \$response->end("Hello World\n");
    }); 

    \$http->start();
EOF

php swoole.php
curl 127.0.0.1:9501
rm swoole.php
And voila! You will see Hello world response in your console.

4. System Configuration

Now let's create the logs directory:

mkdir /var/log/swoole
chown vagrant: vagrant /var/log/swoole

5. Startup Script

We can easily create a startup script which will give us a good way to start, stop or restart the Swoole server, like this:

cat < /etc/systemd/system/swoole.service
[Unit]
Description=Swoole Service
After=network.target
 
[Service]
Type=forking
User=vagrant
Group=vagrant
 
ExecStart=/usr/bin/php //swoole.php
ExecStop=/bin/kill \$MAINPID
ExecReload=/bin/kill -USR1 \$MAINPID
 
RuntimeDirectory=swoole
RuntimeDirectoryMode=0750
PIDFile=/var/run/swoole/swoole.pid
[Install]
WantedBy = multi-user.target
EOF
 
systemctl enable swoole.service
systemctl start swoole.service

A few easy changes to get Swoole LIVE

There are a few pieces of code that we needed to change in order to make the Swoole core working with our application code which as you remember had its ReactPHP core. Please keep in mind that each application is unique, with different structure so in our case the changes that we did were really tiny.

So, what were our differences – the main one is the server initialization:

/** Create the server **/
$server = new swoole_http_server('127.0.0.1', 18101, SWOOLE_BASE);
 
$server->set(array(
  'worker_num' => 4,
  'reactor_num' => 16,
  'daemonize' => 1,
  'max_request' => 0,
  'dispatch_mode' => 1,
  'group' => 'vagrant',
  'log_level'=> 0,
  'log_file' => '/var/log/swoole/swoole.log',
  'pid_file' => '/var/run/swoole/swoole.pid'
));
 
$server->start();

The important settings that will help us have a good asynchronous oroutine-based concurrency networking communication engine are the following:

worker_num - The number of the worker process. If the code of logic is asynchronous and non-blocking, set the worker_num to the value from one times to four times of CPU cores.

reactor_num - This is the number of the reactor thread. In general, the number of the reactor thread is between one and four times the CPU cores.

daemonize - If the value of daemonize is more than 1, the Swoole server will be daemonized. For programs which we need to have running for long time this configuration must be enable - in our case it should be set to true. If the daemonize option is  enabled, the standard output and the errors from the program will be redirected to log_file, otherwise the standard output and errors from the program will be passed to /dev/null.

max_request - If you want to accept unlimited connections you should set this option to 0.

And here is an interesting function that you can use to get the numbers of your server CPU cores, it can ease your work a lot:

function getProcessorCoresNumber() {
  $command = "cat /proc/cpuinfo | grep processor | wc -l";
  return  (int) shell_exec($command);
}

The second difference was meant to create a new router function.

Benefits

So let’s compare the results between PeactPHP and Swoole based on a simple benchmark on the same server with identical worker settings, and of course – with the same PHP structure and functionality. The only thing that’s different is the core:

Swoole:

Running 10s test @ http://swoole.app/ping/
  4 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    52.94ms   45.83ms 246.61ms   80.01%
    Req/Sec     2.21k     1.77k    5.57k    71.00%
  87826 requests in 10.02s, 15.83MB read
Requests/sec:   8766.80
Transfer/sec:      1.58MB

ReactPHP:

Running 10s test @ http://reactphp.app/ping/
  4 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   202.68ms  261.44ms   1.69s    88.96%
    Req/Sec   635.46     85.47     0.91k    75.94%
  25283 requests in 10.05s, 4.00MB read
  Socket errors: connect 0, read 0, write 0, timeout 82
Requests/sec:   2516.24
Transfer/sec:    407.90KB

As you can see the latency is extremely low for Swoole. Also the requests that ReactPHP can handle on this server are almost four times less than what Swoole can handle.

Btw when I run the benchmark which is provided on the Swoole page the results are incredible! Such numbers cannot be seen with ReactPHP:

Running 10s test @ http://127.0.0.1:9501/
  4 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.46ms    4.77ms  40.16ms   84.73%
    Req/Sec    29.46k     5.90k   57.66k    70.28%
  1170955 requests in 10.10s, 195.42MB read
Requests/sec: 115956.06
Transfer/sec:     19.35MB

Of course we’re still trying to improve our code so the application could probably work even faster.

Final words

Swoole has components for different purposes: Server, Task Worker, Timer, Event and Async IO. With these components, Swoole allows you to build many features like web servers, chat messaging servers, game servers and almost anything you want.

Maybe the top advantage of Swoole compared to other complex programming languages, often used on the servers, is that it is very scalable. The event-based network layer in Swoole utilizes the underlying epoll/kqueue implementation in order to handle as far as several thousand connections.