Back to all blogposts

Swoole: Is it Node in PHP or am I wrong?

Adrian Chlubek

Adrian Chlubek

PHP Developer

Many PHP developers are green with envy when thinking about their colleagues who use Node. Asynchronous Node systems are sharing the codebase between protocols and serve them right out of the code. This really makes one wonder about switching to Node. Well, actually something resembling Node in PHP has been added as an extension. And it’s called Swoole.

Node in PHP? What is that Swoole thing?

Give it to us straight, documentation:

Production-Grade Async programming Framework for PHP. Enable PHP developers to write high-performance, scalable, concurrent TCP, UDP, Unix socket, HTTP, and Websocket services in PHP programming language without too much knowledge about non-blocking I/O programming and low-level Linux kernel.

Swoole works as an extension to PHP and it’s written in C. Sounds promising, right? Hosting an HTTP server with PHP? Websockets in PHP itself? Many other possibilities, some looking exotic, all while keeping high performance? Let’s check it out!

How to get it running?

Installation methods on native platforms vary.

For Linux it boils down to one pecl command:
pecl install swoole

For MacOS, brew can be used:
brew install swoole

brew install homebrew/php/php72-swoole

And on Windows, native installation is not possible, but we should use Docker anyway.

Dockerizing Swoole

Of course, the best idea to run the PHP and Swoole is the Docker container. Let’s see how to create a container that will allow us to use Swoole. First, we need a Dockerfile.

FROM php:latest
RUN pecl install swoole
ADD php.ini /usr/local/etc/php
RUN usermod -u 1000 www-data

That was pretty straightforward, wasn’t it?

After extending the official PHP Docker image, installing Swoole with pecl, and copying php.ini – we got it. The last line is a standard Docker fix for MacOS.

When it comes to php.ini config file which is copied, it’s even simpler – requires only one line: extension=swoole.so.

What does it offer?

Swoole has a variety of functionalities. Most of them are asynchronous. Here are some of the most interesting ones (others can be found in Swoole documentation):

  • TCP/UDP server and client,
  • HTTP server and client,
  • Websocket server and client,
  • Redis protocol server and client,
  • MySQL client,
  • Atomics,
  • Filesystem.

Let’s see how to use three of them: HTTP server, Websocket server, and Filesystem. These are the most important ones in my opinion. 

HTTP Server using Swoole

Swoole allows us to run a simple HTTP server that works asynchronously. Here’s the simple code that will also use asynchronous filesystem access to respond with an index.html file to every request it handles.

As we can see, it looks a bit like Node.

First, we create the swoole_http_server object that resembles the HTTP server. Then, we bind two anonymous functions for two events: one for start, which will be executed when the server is started, and another for request, which will be executed for every request. It takes a request and responses objects as parameters.

The request object contains all the data related to the request itself: requested path, headers, and so on. On the other hand, it’s used to provide output, set headers, etc. It’s worth mentioning that these two objects are not PSR conformant, but rather they are custom Swoole objects.

In the request event, an asynchronous file system is used to load the data from a file. Once the data is available, a callback is fired with the content loaded. Then, this content is used to fill the response object and close the response. This sends the data back to the browser effectively.

That looks neat and most of all – works. But what about the performance?

Have a read on npm packages:

HTTP Server benchmark

To test the performance of the HTTP server using Swoole, I’ve created an app in Node – which can do exactly the same thing as the one in Swoole – and also an NGINX server that serves index.html as a static file. All are dockerized in three separate containers.

Then, I used wrk tool to hit these containers hard. The results were astounding.

Swoole results were way better than expected!

That came as a surprise.

I didn’t expect Swoole to overtake NGINX, but it actually did! Node was outdistanced. The raw power of this extension is really impressive, but it fades away with more work being done in the request.

Unfortunately, Swoole has two little drawbacks that make these raw benchmarks irrelevant. We will get to them later.

WebSocket Server using Swoole

As mentioned before, Swoole offers a method to create a WebSocket server. It works asynchronously and follows the same methodology as HTTP and other Swoole functionalities. In my opinion, it’s one of the most important Swoole parts. WebSockets in PHP? I’m in. Let’s see how it looks.

Looks similar to the HTTP server example, doesn’t it?

First, we create the swoole_websocket_server object which resembles the WebSocket server. Then, we bind four anonymous functions to the four events:

  1. Start event that will work just like starting an event from an HTTP server,
  2. Open event that will be executed once a new WebSocket connects,
  3. Message event that will be executed when a WebSocket sends a message to the server,
  4. Close event which runs when a WebSocket disconnects.

Websockets connected to the server are identified by the unique ID that increments with each of the new WebSockets.

Problems encountered whilst using Swoole

It all worked well so far, but there were two problems I encountered during the tests of some solutions using Swoole. These were:

  • no real HTTPS support in the HTTP server,
  • no support for global variables in scripts.

The first one is easy to mitigate. We just need to set up a reverse proxy using NGINX or any load balancer and it’s done. But by doing that, we lose the extreme performance boost that Swoole offers.

The second one is more tricky. Swoole spawns worker processes for handling requests which means that if we create a global variable, its value is independent between threads and it can’t work. Here’s an example showing this behavior.

One would expect the response would return 0, then 1, 2, 3, and so on, but instead it always returns 0.

I reached out to the Swoole authors to check if it’s a bug, but it isn’t. In order to get the behavior we expect, we can set worker_num = 1 in the configuration, but this decreases the performance.

gRPC knowledge galore!

Conclusion

Swoole, like everything, has its bright and dark sides.

I think it’s still a good idea to bring asynchronous programming to PHP. It can be used in various cases, ranging from fast prototyping to concise single-responsibility microservices, and low-latency game servers, and ending up being a backend server for big frameworks.

Promising indeed.

Do you feel like Swoole would be a perfect addition to your project but you don't want to handle this alone?

At The Software House, we have a team of very talented PHP developers who will gladly help you. In order to receive a free consultation, drop us a line. We’re waiting for your message!

You may also like

Online event - CTO Roundtables

[22 Feb ] We're back! Learn how veteran cloud users handle the complexity of cloud management & strategy from our CTO and VP of Technology + 2 reputable cloud experts as guests.

Register for free

What would you like to do?

    This site is protected by reCAPTCHA and the Google
    Privacy Policy and Terms of Service apply.

    We regard the TSH team as co-founders in our business. The entire team from The Software House has invested an incredible amount of time to truly understand our business, our users and their needs.

    Eyass Shakrah

    Co-Founder of Pet Media Group

    Thanks

    Thank you for your inquiry!

    We'll be back to you shortly to discuss your needs in more detail.