Migrating from ReactPHP to Swoole

MTR Design
6 min readDec 30, 2019

--

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.

May be 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.

If you liked this article, then please subscribe to our newsletter. Every month we’ll send out an email with a few articles written by our team about the business, web development, UX design and startups. We hope they will be useful to you and will help you solve some of your digital-related problems.

Originally published on the MTR Design company website.

MTR Design is a small Bulgarian web development company with expertise in a wide range of technologies (PHP, Python, Golang, Lua, Salesforce, Node, React and React Native, Angular, VueJS, iOS and Android development). We are currently open for new projects and cooperations, so if there are any projects we can help with, please react out (use the contact form on our website, or email us at office@mtr-design.com) and we will be happy to discuss them.

--

--

MTR Design

MTR Design, mtr-design.com, is a Bulgarian software development consultancy with proven track record in delivering complex, scalable web solutions.