Some PHP developers look green with envy at the thought of colleagues who can use Node. Asynchronous Node systems can share code bases across different protocols and provide services outside of the code. This really wants someone to switch to Node development. There is actually a PHP equivalent of Node, and it’s included in the PHP extension called Swoole.


Node in PHP? What exactly is Swoole?

I’ll quote a definition of Swoole from the official documentation:

Swoole: PHP asynchronous network communication engine for production environments.


Enables PHP developers to write high-performance, scalable asynchronous and concurrent TCP, UDP, Unix sockets, HTTP, and WebSocket services without deep knowledge of non-blocking I/O programming and the rudimentary Linux kernel.

Swoole is written in C and exists as a basic extension to PHP. That doesn’t sound bad, does it? Running HTTP services in PHP? Websockets in PHP? There are other possibilities. Isn’t that sexy? And all of these will maintain extremely high performance, let’s take a look!


How do I get it to work?

The installation method varies according to platforms.

For Linux, you only need to run a PECL command:

pecl install swoole

MacOS users can use the brew command:

brew install swoole


brew install homebrew/php/php72-swoole


By the time of translation, Brew has officially removed all PHP extensions, please install using PECL.

Installation on Windows is not supported, but Docker can be used.


Use Docker to run Swoole

Without a doubt, the best way to run PHP + Swoole is Docker. Let’s look at how to create a container that contains Swoole. First, we need to create a Dockerfile.

FROM php:latest\


RUN pecl install swoole\


ADD php.ini /usr/local/etc/php\


RUN usermod -u 1000 www-data

This seems pretty straightforward. Based on the official PHP Docker image, install Swoole using PECL, then copy php.ini into the image — done. The last line is a general permission repair command for MacOS Docker.

As for the copied php.ini configuration file, it only needs one line:

extension=swoole.so


What can Swoole do?

Swoole has many functions, most of which are performed asynchronously. Here are some of the most interesting bits (others can be found in the Swoole documentation) :

  • TCP/UDP server and client,
  • HTTP server and client,
  • Websocket server and client,
  • Server and client based on Redis protocol,
  • MySQL client,
  • Atomicity,
  • File system.

Let’s take a look at HTTP services, Websocket services, and file systems. In my opinion, these are the most important features.


Implement HTTP service based on Swoole

Swoole-based implementation of a simple asynchronous HTTP service requires very little code. Here is an example code that uses an asynchronous file system to read the index.html file and return it as a response for each request it processes.

<? phpchdir(__DIR__);
$http = new swoole_http_server('php', 8080);
$http->on('start'.function ($server) {
    echo "Server has been started! \n";
});
$http->on('request'.function ($request.$response) {
    swoole_async_readfile('index.html'.function($filename.$content) use ($response) {
        $response->header('Content-Type'.'text/html');
        $response->end($content);
    });
});
$http->start();Copy the code

As you can see, this code looks a bit node.js style.

First, we create a SWOole_HTTP_server object that looks like an HTTP service. Next, bind two asynchronous callback functions to the following events: one for startup, which will be called when the service starts; The other is for requests, which will be called each time a request is received, and takes $request and $response.

The $Request object contains all the data associated with the request: the request Path, Headers, and so on. $response is used to provide the output, set the response, and so on. It’s worth noting that neither of these objects is PSR compliant and Swoole is a custom. In a request event, the asynchronous request file system is used to load data from a file. Once the data is available, the callback is triggered after the data has been loaded. This data is then loaded into the response body and the response is closed. This will effectively send the data back to the browser.

It looks neat and, most importantly, works. How does it perform?


The HTTP Server standard

To use Swoole to test HTTP server performance, I created an application in Node – which could be identical to the Swoole application – and a server that would serve index.html as a static file. It all runs in 3 separate containers.

I then pressure tested the containers using the WRK tool. The results were startling.





Swoole works much better than expected!

This is surprising. I didn’t think Swoole would surpass Nginx, but it did! This is also far more than Node. The original functionality of this extension was really impressive, but it petered out as more work was done in the request. Unfortunately, Swoole has two small flaws that make them a bit out of line with the original criteria. We’ll find them later.


Use Swoole in the Websocket service

As mentioned earlier, Swoole provides a way to create a Websocket server. It works asynchronously, following the SAME HTTP protocol and functions as part of the Swoole method. In my opinion, it is one of the most important Swoole components. So let’s see what websockets look like in PHP running. Let’s see how it turns out.

<? php$server = new swoole_websocket_server('php', 9501);
$server->on('start'.function (swoole_websocket_server $server) {
    echo "Server has been started! \n";
});
$server->on('open'.function (swoole_websocket_server $server.$request) {
    echo "websocket: new connection, id: {$request->fd}\n";
});
$server->on('message'.function (swoole_websocket_server $server.$frame) {
    echo "websocket: {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $server->push($frame->fd, "Replying, you sent " . $frame->data);
});
$server->on('close'.function (swoole_websocket_server $server.$fd) {
    echo "websocket: connection with id {$fd} has been closed\n";
});
$server->start();Copy the code

An example that looks similar to an HTTP server.

First, we create a swoole_websocket_server object similar to a Websocket server. Then, we bind four anonymous functions to four events. The first startup event, which will work like the HTTP server’s startup event. The second run event, which is executed after connecting to another WebSocket. The third message event is executed when the WebSocket sends a message to the server. Finally – the shutdown time is executed when the Websocket is disconnected.

The ID is the unique identifier that a Websocket uses to connect to the server, increasing with each new WebSockets.


Problems with using Swoole

So far, this has worked well, but there are two problems with using Swoole to test some of the solutions. Let me list it:

  • There is no real SUPPORT for HTTPS in HTTP servers,
  • Global variables are not supported in scripts.

The first problem is easy to solve. We just need to set up the reverse proxy using Nginx or any load balancing device and we’re done. But by doing so, we lose the extreme performance Swoole provides.

The second problem is trickier. Swoole generates the worker process used to process HTTP requests, which means that if we create a global variable, its value is independent between threads, and it doesn’t work. The following code shows where the problem lies.

<? php$counter = 0;
$http = new swoole_http_server('php', 8080);
$http->on('request'.function ($request.$response) use ($counter) {
    $response->header("Content-Type"."text/plain");
    $response->end($counter+ +); });$http->start();Copy the code

The message in the response is expected to return 0, then 1, 2, 3, and so on, but it always returns 0.

I went to Swoole’s author to check if it was a bug, but it wasn’t. To get the desired behavior, we could set worker_Num = 1 in the configuration, but this would degrade some of the performance.


conclusion

Swoole, in general, has bright sides and dark corners. I think it’s still a good idea to introduce asynchronous programming to PHP. It can be used in a variety of situations, including rapid prototyping, simple microservices with a single responsibility, low-latency game servers, and backend servers as large frameworks. Promising indeed.


Hope the above content can help you, join my official group click here. Get more swoole learning materials and video source notes.