Why Roadrunner is the ideal application server for PHP applications

Pavel Buchnev
12 min readFeb 7, 2023

--

Many PHP frameworks started providing integration with the RoadRunner application server, but only with HTTP plugin. This limited exposure to its capabilities has resulted that many PHP developers may not be aware of the full range of features offered by it.

Although the HTTP plugin is what many PHP frameworks integrate with, it is just the beginning of what RoadRunner has to offer. The other plugins provide numerous benefits, including increased performance, better scalability, and more robust application design.

Plugins

RoadRunner is designed to be like a central processor for PHP applications, helping developers create faster, more responsive and robust applications with ease.

Whether you’re building a complex web app or a simple PHP script, it has everything you need to deliver top-notch performance and functionality. It is a great tool for developers looking to streamline their application development workflow.

Easy configuration

Setting up and managing your applications with RoadRunner is a breeze thanks to its simple and easy-to-use .rr.yaml configuration file.

version: '2.7'

rpc:
listen: tcp://127.0.0.1:6001

server:
command: "php app.php"
relay: pipes

Read documentation about RoadRunner configuration on the official site.

The example shown above gives you an idea of what a basic RoadRunner configuration looks like. In the next sections we will explore plugins with config examples and you will see how it’s too easy to configure them.\

HTTP

The HTTP plugin offers you robust support for handling HTTP requests, serving as the backbone of your application’s web-based functionality. In addition to supporting traditional HTTP connections. It also provides support for secure HTTPS connections and the HTTP/2 protocol.

One of the benefits of this plugin is its automatic TLS certificate management using Let’s Encrypt, allowing you to implement secure connections without manually managing certificates. Furthermore, it provides a variety of middleware, such as Gzip, CORS, and X-Sendfile, giving you increased control over file response management and optimizing the performance of file delivery from your application to the client.

To enable plugin you just need to add the following code to .rr.yaml:

http:
address: 127.0.0.1:8080
ssl:
address: :443
redirect: true
acme:
certs_dir: certs
email: user@site.com
domains:
- site.com

GRPC

The plugin enables you to handle GRPC requests within your PHP applications, unlocking the full potential of this modern framework. GRPC is an open-source technology for building scalable, distributed systems that offers low latency, high performance, and bi-directional streaming capabilities.

GRPC uses a binary protocol, making it faster than traditional REST-based APIs that rely on text-based protocols such as HTTP and JSON. It also supports strict data schemas and automatic generation of DTOs, providing a more organized and efficient way of transferring data. Furthermore, it is language agnostic, enabling you to call services from other languages and vice-versa in an easy and fast way. This can help you build a more flexible and scalable microservices architecture.

To enable plugin you just need to add the following code to .rr.yaml:

grpc:
listen: "tcp://127.0.0.1:9001"
proto:
- "users.proto"

Jobs (queues)

The Jobs plugin is a highly advanced and efficient plugin that sets it apart from traditional PHP frameworks. The QoS enables RoadRunner to manage job tasks from queue storage automatically and distribute them to available consumers, thus removing the need for consumers to actively retrieve jobs from the queue. This results in a highly concurrent and scalable event bus, capable of consuming up to 100k events per second with minimal configuration.

It provides numerous advantages over traditional PHP implementations of queue orchestration and supports multiple storage backends, including Redis, RabbitMQ, and Kafka. Firstly, it reduces the workload on consumers, freeing them from the task of actively retrieving jobs from the queue, and allowing them to focus solely on job processing. This results in improved application efficiency and faster job processing times. Secondly, it ensures that job tasks are distributed evenly among consumers, preventing overloading and optimizing overall application performance. Additionally, it allows for dynamic scaling of consumers, enabling developers to manage the workload as the needs of the application change.

To enable plugin you just need to add the following code to .rr.yaml:

jobs:
consume: [ "in-memory" ]
pipelines:
in-memory:
driver: memory
config:
priority: 10
prefetch: 10

Temporal

The plugin offers seamless integration with the Temporal workflow engine, enabling developers to effectively manage complex workflows within their PHP applications. It provides a user-friendly API for defining, executing, and monitoring workflows, and is capable of handling tasks that span days, weeks, or even months. It boasts robust fault-tolerance, resuming workflows automatically in the event of a task failure, ensuring that the workflow is completed despite unexpected events. The decoupled implementation of the workflow from the rest of the application allows for easy modification and testing without affecting the rest of the application. Moreover, the Temporal workflow engine supports workflows and activities written in multiple programming languages, including Go, Java, TypeScript, and more, making it a versatile solution for building complex workflows.

To enable plugin you just need to add the following code to .rr.yaml:

temporal:
address: 127.0.0.1:7233

Key-Value (Cache broker)

The plugin allows developers to manage application cache, including storing, obtaining, deleting and clearing data using cache brokers like Redis, Memcache, memory and more. It provides an easy to use caching layer that works with a variety of brokers, and is PSR-16 compatible.

To enable plugin you just need to add the following code to .rr.yaml:

kv:
in-memory:
driver: memory
config: {}
users:
driver: redis
config:
addrs:
- "localhost:6379"

Centrifuge

The plugin offers comprehensive integration with the high-performance Centrifugo websocket server, enabling real-time communication and scalability between client and server. With this integration, developers can effortlessly integrate the Centrifugo server into their PHP application and benefit from its capabilities without having to manage the underlying infrastructure. Furthermore, the Centrifugo server’s pub-sub model provides robust functionality and flexibility, allowing for seamless transmission and receipt of messages through RPC calls.

It allows developers to separate the handling of connect, subscribe, RPC, and refresh client events across different servers, thereby providing high scalability.

To enable plugin you just need to add the following code to .rr.yaml:

centrifuge:
proxy_address: "tcp://127.0.0.1:30000"
grpc_api_address: tcp://127.0.0.1:30000

Metrics (prometheus)

The plugin allows developers and devops to monitor the performance of the RoadRunner server and collect application metrics. These metrics can be sent to the Prometheus server, which provides a centralized place for collecting, processing and visualizing metrics. With Grafana dashboards, devops can gain valuable insights into the performance of RoadRunner instances and PHP application, such as the total number of registered users, the amount of money earned and so on.

This plugin provides a simple way for developers to send and visualize important application metrics, helping them make informed decisions.

To enable plugin you just need to add the following code to .rr.yaml:

metrics:
address: localhost:2112
collect:
registered_users:
type: counter
help: "Total number of registered users."
money_earned:
type: gauge
help: "The amount of earned money."
labels: ["project"]

To send metric from the application:

$metrics = new Spiral\RoadRunner\Metrics\Metrics($rpc);
$metrics->add('registered_users', 1);

Service

The plugin allows developers to run external processes, similar to how supervisor operates. By utilizing it, developers can offload the management of external processes to RoadRunner.

To enable plugin you just need to add the following code to .rr.yaml:

service:
meilisearch:
service_name_in_log: true
remain_after_exit: true
restart_sec: 1
command: "./bin/meilisearch"
centrifuge:
service_name_in_log: true
remain_after_exit: true
restart_sec: 1
command: "./bin/centrifugo --config=centrifugo.json"

Status

The plugin allows developers to monitor the health of the RoadRunner workers through a simple HTTP endpoint. The endpoint returns the health status of the workers and provides information on whether the workers are operating normally or not. This information can be used to ensure that the application is running smoothly and to detect and resolve issues quickly if they arise.

By accessing the health-check endpoint at:

http://127.0.0.1:2114/health?plugin=http

devops can have a real-time view of the health of the workers.

To enable plugin you just need to add the following code to .rr.yaml:

status:
address: 127.0.0.1:2114

Fileserver

The plugin can be used to serve files from different directories by using URL prefixes. This allows for fine-grained control over the visibility and access to specific files and directories. For example, you can map the URL prefix http://127.0.0.1:10101/assets to the /app/public/assets folder to provide public access to static assets, or map the URL prefix http://127.0.0.1:10101/user to the /app/storage/users/avatars folder to serve user uploaded files.

It provides the following abilities:

  1. ETag calculation helps in validating the integrity of cached files.
  2. Body streaming allows the plugin to stream request bodies for files larger than 4KB, which can improve performance and reduce memory usage.
  3. Compression of the response can reduce the amount of data transmitted over the network, which can reduce network costs.

To enable plugin you just need to add the following code to .rr.yaml:

fileserver:
address: 127.0.0.1:10101
calculate_etag: true
weak: false
stream_request_body: true
serve:
- prefix: "/assets"
root: "app/public/assets"
- prefix: "/fuser"
root: "app/storage/users/avatars"

TCP

The plugin provides support for TCP communication, enabling developers to build custom protocols and applications.

To enable plugin you just need to add the following code to .rr.yaml:

tcp:
servers:
monolog:
addr: 127.0.0.1:9913
delimiter: "\n"
var-dumper:
addr: 127.0.0.1:9912
delimiter: "\n"
smtp:
addr: 127.0.0.1:1025

Application logger

The plugin allows you to send PHP application logs to the RoadRunner server through an RPC connection. It provides several advantages over traditional logging methods. First, since RoadRunner is non-blocking, it can store logs in a file without blocking the PHP process. This results in a faster and more efficient logging process. Secondly, by collecting logs in a centralized location within RoadRunner, it becomes easier for you to monitor and analyze the logs of your PHP application.

Performance

The secret to success lies in RoadRunner’s architecture. Unlike PHP, it is written in Golang and takes advantage of multithreading to achieve performance benefits, including its efficient concurrency model and the ability to handle heavy loads without sacrificing performance.

The use of Golang also makes it possible for developers to write parts of their application as plugins for even better performance.

If these tasks were implemented in PHP, the performance would likely be significant slower and less efficient, due to the limitations of PHP’s single-threaded nature and its dynamically typed, interpreted language.

One of the standout features of RoadRunner is its ability to boost the performance of PHP applications. This is achieved by bootstrapping an application once and then exchanging information between the server and workerthrough the utilization of IPC (Inter-Process Communication) protocol with lightning speed. This means that it is ideal for long-running applications that need to perform at their best at all times.

This design choice results in a number of advantages, including:

  1. Reduced latency: The use of low-level communication methods such as sockets or IPC enables faster and more direct communication between the worker and server, reducing the amount of time required to send and receive data.
  2. Increased performance: The optimized communication between the worker and server results in improved overall application performance, as data is transmitted and processed more quickly and effectively.

Scalability

RoadRunner is designed with scalability in mind, offering a flexible and efficient architecture that enables developers to distribute different aspects of the application across multiple instances.

For example, one instance of RoadRunner can be dedicated to handling HTTP requests, while another instance can be used for cache management. Another instance can be used for queue management, such as pushing jobs into a queue without consuming them, and additional instances can be added for consuming the queue. This distributed approach allows for optimal resource utilization, as each instance can be optimized for a specific task, leading to improved overall performance.

Morover, if you run two several RoadRunner instance with http plugin on one server they will balance requests among each othehr.

With RoadRunner, developers can use as many instances as needed and communicate between them using the Remote Procedure Call (RPC) protocol.

Monitoring

Metrics and Status plugins offer separate and distinct capabilities for monitoring the performance and health of the application. The Metrics plugin, powered by Prometheus, provides developers and devops with the ability to collect and visualize key application metrics in a centralized location. With Grafana dashboards, devops can easily see how their application is performing and make informed decisions based on the data.

On the other hand, the Status plugin provides real-time visibility into the health of RoadRunner workers through a simple HTTP endpoint. By checking the endpoint at “http://localhost:2114/health?plugin=http", devops can quickly determine the status of each worker and take action as needed to ensure that the application is running smoothly.

There is also Birddog — a monitoring server developed specifically for RoadRunner. It provides a unified platform for monitoring the workers, services, and jobs of RoadRunner instances. One of the key benefits is its ability to aggregate data from multiple instances, providing a centralized view of the entire application infrastructure.

Microservices

RoadRunner is designed to be highly versatile and well-suited for microservices architecture. It is capable of running in a variety of environments, including AWS Lambda, Docker, and Kubernetes. This versatility makes it an ideal choice for developers looking to build scalable and efficient microservices applications.

AWS Lambda

In AWS Lambda, it can be used to handle high-traffic, event-driven workloads, providing fast and reliable response times. The ability to run in a serverless environment means that developers can save on infrastructure costs and focus on delivering the best user experience possible.

Docker

When running in Docker, it can be easily containerized and deployed in a variety of environments, including on-premise, in the cloud, or on-the-edge. This makes it a great choice for organizations looking to modernize their infrastructure.

Kubernetes

In Kubernetes, it can be deployed and managed as a set of containers, providing the benefits of orchestration and automation. This makes it possible for organizations to scale their applications up or down as needed, without sacrificing performance or reliability.

Custom builds

RoadRunner provides a number of plugins as well as the ability to create custom builds using the Velox tool. With Velox, you can create a configuration file that lists all the required plugins you need for your build. This allows you to easily tailor the functionality of RoadRunner to meet the specific needs of your project. By defining the plugins you want to use in a configuration file, you can create custom builds that are optimized for your particular use case, with the plugins you only need.

Here is an example of config:

[velox]
build_args = ['-trimpath', '-ldflags', '-s -X github.com/roadrunner-server/roadrunner/v2/internal/meta.version=${VERSION} -X github.com/roadrunner-server/roadrunner/v2/internal/meta.buildTime=${TIME}']

[roadrunner]
ref = "v2.12.1"

[github]
[github.token]
token = "${RT_TOKEN}"

[github.plugins]
# LOGS
appLogger = { ref = "v3.2.0", owner = "roadrunner-server", repository = "app-logger" }
logger = { ref = "v3.2.0", owner = "roadrunner-server", repository = "logger" }

# CENTRIFUGE BROADCASTING PLATFORM
centrifuge = { ref = "v3.2.0", owner = "roadrunner-server", repository = "centrifuge" }

# WORKFLOWS ENGINE
temporal = { ref = "v3.0.0", owner = "temporalio", repository = "roadrunner-temporal" }

# METRICS
metrics = { ref = "v3.3.0", owner = "roadrunner-server", repository = "metrics" }

# HTTP + MIDDLEWARE
http = { ref = "v3.2.0", owner = "roadrunner-server", repository = "http" }
gzip = { ref = "v3.2.0", owner = "roadrunner-server", repository = "gzip" }
headers = { ref = "v3.2.0", owner = "roadrunner-server", repository = "headers" }
static = { ref = "v3.2.0", owner = "roadrunner-server", repository = "static" }

# RELOAD (for the DEV)
reload = { ref = "v3.2.0", owner = "roadrunner-server", repository = "reload" }

# SERVER
server = { ref = "v3.2.0", owner = "roadrunner-server", repository = "server" }

# JOBS
jobs = { ref = "v3.2.0", owner = "roadrunner-server", repository = "jobs" }
kafka = { ref = "v3.0.4", owner = "roadrunner-server", repository = "kafka" }

# KV
kv = { ref = "v3.2.0", owner = "roadrunner-server", repository = "kv" }
memory = { ref = "v3.3.0", owner = "roadrunner-server", repository = "memory" }
redis = { ref = "v3.2.0", owner = "roadrunner-server", repository = "redis" }

# FILESERVER (static files)
fileserver = { ref = "v3.2.0", owner = "roadrunner-server", repository = "fileserver" }

# gRPC plugin
grpc = { ref = "v3.2.0", owner = "roadrunner-server", repository = "grpc" }

# HEALTHCHECKS + READINESS CHECKS
status = { ref = "v3.2.0", owner = "roadrunner-server", repository = "status" }

[log]
level = "debug"
mode = "development"

PHP Framework designed for RoadRunner

The Spiral Framework is a top-notch framework build on PHP 8.1 and made by the R&D team at Spiral Scout. It’s designed to work with RoadRunner, which makes it even faster. In fact, it’s 2x quicker than other PHP frameworks. It’s great for building and managing big enterprise apps, and has been around for over 12 years with a strong design.

Spiral designed to facilitate the development of long-running applications while ensuring efficient memory management and the prevention of memory leaks. This is achieved through the utilization of advanced memory management techniques. It designed to handle a wide range of request types, including HTTP, gRPC, TCP, Queue Job consuming, and Temporal. It operates by running workers only once when it is initiated and then directing requests to a dispatcher based on their type. Each worker is isolated and works independently, following a “share nothing” approach where resources are not shared between workers.

Note Read documentation about framework on the official site.

In conclusion

In conclusion I want to tell, that RoadRunner is much more than just an HTTP server.

It is a powerful and versatile application server that provides PHP developers with a wealth of tools and plugins to take their applications to the next level. Whether you need to manage queues, cache, metrics, or GRPC requests, RoadRunner has you covered, making it an essential tool for modern PHP development.

--

--

Pavel Buchnev

Senior PHP Developer | Contributor to Spiral Framework 🚀 | Enthusiast of RoadRunner & long-running applications | Creator of Buggregator