--- 
canonical: 'https://mwop.net/blog/2018-10-16-swoole.html'
title: 'Async Expressive with Swoole'
author: "[Matthew Weier O'Phinney](https://mwop.net)"
created: '2018-10-16T16:00:00-05:00'
updated: '2018-10-17T08:30:00-05:00'
tags:
  - async
  - node
  - php
  - programming
  - psr-15
  - psr-7
  - swoole

---
Have you used [Node.js](https://nodejs.org/)?

For those of my readers unfamiliar with Node.js, it's a server-side JavaScript
framework that provides the ability to create, among other things, network
services. To do so, it provides an event loop, which allows for such things as
asynchronous processing.

In the PHP ecosystem, a group of Chinese developers have been creating an
extension that provides many of the same capabilities as Node.js. This
extension, called [Swoole](https://www.swoole.co.uk), allows you to create web
servers with asynchronous capabilities. In many cases, the asynchronous
capabilities are handled via coroutines, allowing you to write normal,
synchronous code that still benefits from the asynchronous nature of the system
event loop, allowing your server to continue responding to new requests as they
come in!

We've been gradually adding and refining our [Swoole support](https://docs.zendframework.com/zend-expressive-swoole/)
in [Expressive](https://docs.zendframework.com/zend-expressive/), and recently
issued a stable release that will work with any [PSR-15](https://www.php-fig.org/psr/psr-15)
request handler. In this post, I'll enumerate what I feel are the reasons for
considering Swoole when deploying your PHP middleware application.





I feel there are three key advantages to Swoole, and, by extension, any async
PHP runtime:

- Application-specific servers
- Performance
- Async processing

## Application-specific servers

There are a few general architectures for applications:

- A single web server sitting in front of many web applications.
- A single web server sitting in front of a single web application.
- A load balancer sitting in front of many servers. Some servers might serve the
  same application, to provide redundancy. (Often, today, these may even be
  identical docker containers.)

![nginx serving many PHP sites](https://uploads.mwop.net/nginx-php-many.png)

The first scenario is common in internal networks and development, and in many
shared hosting scenarios. It's generally considered less secure, however, as a
vulnerability in one application can potentially escalate to affect all
applications hosted on the server. Additionally, it means that any updates to
PHP versions must be tested on all applications, which often means updates are
few and far between &mdash; which is also problematic from a security
standpoint.

When you want to isolate the environment, you'll move to a single web server,
single PHP application model:

![nginx serving a single PHP site](https://uploads.mwop.net/nginx-php-single.png)

And when you start scaling, this becomes a load balancer sitting in front of
many of these web server/PHP application pairs:

![A load balancer in front of many nginx+php sites](https://uploads.mwop.net/load-balancer-nginx-php.png)

In each of these last two scenarios, there's one thing I want to point out: your
application consists of at least two distinct services: the PHP processes, and a
web server.

> You may have other services as well, such as an RDBMS or document database,
> cache, search, etc. But generally these are on separate servers and scaled
> separately. As such, they're outside of this discussion.

In these scenarios, this means each "server" is actually a composite. And when
you are adding redundancy to your architecture, this adds significant
complexity. It's one more process on each and every node that can fail, and
additional configuration you need when deploying.

When we start thinking about microservices, this becomes more problematic.
Microservices should be quick and easy to deploy; one service per container is
both typical and desired.

What Swoole lets us do is remove one layer of that complexity.

![A load balancer in front of php servers](https://uploads.mwop.net/load-balancer-php.png)

We can have a service per container, and that container can be built with only
PHP. We start the Swoole HTTP server, and it's ready to go. We then tell the
reverse proxy or load balancer how to route to it, and we're done.

This is useful in each of the scenarios, including the one web server/mulitiple
applications scenario, as we can have different PHP runtimes per application.
Our "web server" becomes a reverse proxy instead.

Application-specific servers allow us to simplify our deployment, and ship
microservices quickly.

## Performance

Remember when PHP 7 came out, and it was like doubling the performance of your
application?

What if you could do that again?

In our initial benchmarks of Expressive applications, we found that they
performed four times better under Swoole than under a traditional nginx+php-fpm
pair. More interesting: when benchmarking with a high number of concurrent
requests, we also found that Swoole had fewer failed requests. This means you
get both better performance and better resilience!

And the hits keep rolling in: when we enabled Swoole's coroutine support and
benchmarked endpoints that made use of functionality backed by that coroutine
support, we observed up to a ten-fold increase!

> The coroutine support covers primarily network I/O operations. As such,
> operations that hit cache servers, use PDO, or make web requests benefit from it
> immediately, with no changes to your code.

Swoole makes this possible in a couple of ways. First, because you are firing up
a server exactly once, you lose the price of bootstrapping your application that
you normally incur on each and every request; your application is bootstrapped
from the moment you start accepting requests. Bootstrapping often accounts for
the greatest single amount of resource usage in your application.

Second, Swoole runs as an event loop, just like Node.js, allowing it to defer
processing of long-running requests in order to respond to new, incoming
requests. This leads into my last point.

## Async processing

Swoole's event loop provides async functionality to PHP applications. While a
number of userland libraries have popped up over the past five years or so that
provide async capabilities for PHP, Swoole's is done as a native C extension,
and works regardless of the operating system.

When you have an event loop, you can defer processing, which allows the server
to respond to additional requests. Commonly, deferment can be explicit:

```php
public function handle(ServerRequestInterface $request) : ResponseInterface
{
    $ts = new DateTimeImmutable();
    \Swoole\Event::defer($this->createCacheDeferment($ts));
    return new EmptyResponse(202);
}

public function createCacheDeferment(DateTimeImmutable $ts) : callable
{
    return function () use ($ts) {
        sleep(5);
        $now = new DateTimeImmutable();
        $item = $this->cache->getItem('ts');
        $item->set(sprintf(
            "Started: %s\nEnded: %s",
            $ts->format('r'),
            $now->format('r')
        ));
        $this->cache->save($item);
    };
}
```

In this example, we calculate the content to return, defer caching, and return a
response immediately. This means your user does not need to wait for you to
finish caching content.

Logging is another use case. In the Expressive Swoole bindings, we do access
logging after we mark the response complete. This ensures that logging does not
impact response times.

Another use case is webhooks. Your application can accept a payload immediately,
but finish processing of it after sending the response back to the client.

Swoole also provides async-enabled versions of common filesystem operations,
Mysql, Redis, and an HTTP client. In each of these, you provide a callback
indicating what should be done once the operation is complete:

```php
use Swoole\Http\Client as HttpClient;

$client = new HttpClient('https://example.com');
$client->setHeaders([
    'Accept' => 'application/json',
    'Authorization' => sprintf('Bearer %s', $token),
]);

// Make the request, telling it what code to execute once
// it is complete:
$client->get('/api/resource', function ($response) {
    // process the response 
});

// This code executes before the request completes:
$counter++;
```

Code like the above has led to the term "callback hell" when you have many such
deferments that depend on each other.  So, what do you do if you want your
code to be "non-blocking", but don't want to write callbacks all the time?
Well, recent versions of Swoole allow you to enable coroutine support for most
I/O operations. What this means is that you can write your code just like you
would in a synchronous environment, but whenever code that triggers a coroutine
occurs, the server will advance the event loop, allowing it to answer additional
requests before the current one completes its work, and then resume execution
once it has.

```php
// This spawns a coroutine:
$statement = $pdo->query($sql);
```

Async functionality may not directly improve the performance of your
application, but it **will** let your application answer more requests, allowing
you to handle greater volumes of traffic!

## zend-expressive-swoole

We released [zendframework/zend-expressive-swoole 1.0.0](https://docs.zendframework.com/zend-expressive-swoole/)
two weeks ago. This library acts as a zend-httphandlerrunner
`RequestHandlerRunner` implementation, which means:

- It can be used with any PSR-15 application.
- It can be used with any PSR-7 implementation.

In other words, if you want to use Swoole with the upcoming Slim 4 or with
[equip/dispatch](https://github.com/equip/dispatch) or with
[northwoods/broker](https://github.com/northwoods/broker) or any of the myriad
PSR-15 dispatch systems out there, you can.

The library provides some interesting features for users:

- Serving of static resources, with HTTP client-side caching headers.
- Configurable logging.
- Abiility to restart worker processes.

I've been running applications on versions of it for the past two months, and
have noted that it has been stable and reliable. I definitely think it's worth
giving it a spin!

## Fin

I'm really excited about the possibilities of Swoole and other async systems, as
I think they afford us better performance, better reliability, and the ability
to defer functionality that doesn't need to complete before we respond to
clients. I'd love to hear YOUR experiences, though, particularly in the form of
blog posts! Send me a link to a blog post via a comment, or by [tweeting at
me](https://twitter.com/mwop), and I'll add it to the [ZF newsletter](https://tinyletter.com/mwopzend).

### Updates

- 2018-10-17: Fixed typo in first sentence.
