http://www.phparch.com \ May 2019 \ 3
Deploying ReactPHP ApplicationsFEATURE
Deploying ReactPHP Applications
Cees-Jan Kiewiet
In this article, we’re going to deploy a ReactPHP^1 application and discuss all the things involved
with it. This setup is the way I’m currently deploying my applications, but that doesn’t mean I’ll
keep using this method. I’m already looking at Docker^2 and k8s^3. However, the information in
this article helps you whether you deploy the way I describe here or another deployment tool. To
fully cover everything in this article would be too much so I’ll be focusing on the deployment and
everything directly related to it. I’ll skim over the rest and encourage you to dive into that yourself
after reading this article.
1 ReactPHP: https://reactphp.org
2 Docker: https://www.docker.com
3 k8s: https://kubernetes.io
4 reactor pattern: https://phpa.me/wikip-reactor-pattern
5 PCNTL: https://php.net/book.pcntl
6 stream_select: https://www.php.net/stream_select
7 ext-uv: http://pecl.php.net/package/uv
About ReactPHP
ReactPHP is a PHP implementation
of the reactor pattern^4 providing the
tools for non-blocking I/O in pure PHP,
with support for extensions providing
better performance. With these tools
non-blocking HTTP application
servers can be built, microservices
consuming message queues, or pretty
much anything non-blocking handling
thousands of items per second.
Building Your Application
The small application we’re going to
deploy is a message queue consumer
which bulk imports messages onto a
database platform. We won’t be going
into how to build that application—
that’s something for another time.
Building to Last
Whether you’re building your app to
run on a VPS as in this article, to run on
bare-metal, or on k8s your application
should be built to last and be able to run
for months without ever going down.
On the other hand, it must be able to
spin up quickly and be ready for work.
Running it on k8s also means that it
can be killed at any moment and there
could be one, two, 15, or more instances
running doing the same work.
SIG*
Starting the loop is one thing, graceful
shut down is another. Handling signals
using PCNTL^5 or directly from an event-
loop extension is the most common
way to be notified of a shutdown event.
(This can be anything from a server
restart to a new deployment replacing
the current instance, to a pod resca-
ling.) When a signal comes in we’ll start
doing the following things in order:
- Stop accepting any new (HTTP)
connections, or any other new
operations - Finish off or cancel any work
- Flush/store any state
- Complete any other operations and
close database/MQ/API connec-
tions
The reason we do it in this order is to
ensure nothing new is coming, let the
rest of the work finish, and close any
required connections needed to do the
work to ensure our application shuts
down cleanly.
Picking an Event Loop
The way we constructed the event
loop factory is to pick the most perfor-
mant loop available on your system, and
if no extension is available, it falls back
to the default stream_select^6 based
loop. The default loop, however, comes
with a few drawbacks if you haven’t
installed any event loop extension. The
stream_select based loop is limited to a
maximum of 1024 open file descriptors.
This limit cannot be changed without
recompiling PHP, so we always strongly
suggest installing an event loop exten-
sion like ext-uv^7 to work around that
limitation and get more performance
along the way. As a bonus all the event
loop extensions have built-in timers
and signal handling baked into the
extension, the stream_select based
loop does that in userland PHP making
it less precise and snappy than the event
loop extensions. While the stream_
select based loop is a fully functional
event loop, we recommend using it only
in development and use an event loop
extension in production.
ulimit
The default event loop isn’t the
only safety net limiting the open file
descriptor count. Operating systems
often also have such safety measures
built in. Linux comes with two of those:
a hard limit of open file descriptors
which cannot be changed, and a soft
limit of open file descriptors which can
be changed and raised to the hard limit
by the user itself. Once your process
hits the soft limit, it starts erroring
out, fails to include userland exception