Home Web Development High Performance with Laravel Octane

High Performance with Laravel Octane

By Roberto Butti
books-svg-icon Book
eBook $28.99 $19.99
Print $36.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $28.99 $19.99
Print $36.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Chapter 1: Understanding the Laravel Web Application Architecture
About this book
Laravel Octane is a very powerful component in the Laravel ecosystem that can help you achieve remarkable app performance. With Laravel Octane, you will find tools (queues, cache, and tables) that facilitate a new asynchronous approach for improving application performance. This book highlights how Laravel Octane works, what steps to take in designing an application from the start, what tools you have at your disposal, and how to set up production environments. It provides complete coverage of the strategies, tools, and best practices to make your apps scalable and performant. This is especially important as optimization is usually the overlooked part in the application development lifecycle. You will explore the asynchronous approach in Laravel and be able to release high-performing applications that have a positive impact on the end-user experience. By the end of this book, you will find yourself designing, developing, and releasing high-performance applications.
Publication date:
January 2023
Publisher
Packt
Pages
204
ISBN
9781801819404

 

Understanding the Laravel Web Application Architecture

This book is for Laravel developers who would like to make or design their Laravel web application in a more scalable way and make it more performant.

This book aims to provide you with knowledge, suggestions, and explanations on how to improve the software architecture of a web application, starting from a typical PHP web application architecture to a more scalable and performant architecture.

It provides a 360-degree overview of what it takes to design and build a performant application with Laravel Octane. We’ll see why Laravel Octane is suitable for designing and building a performant application. The book also covers the different tools used by Laravel Octane, such as Open Swoole and RoadRunner, and lists and describes the various features and differentiating elements. But the most important thing is to enable you to understand why and when to use Open Swoole or RoadRunner.

But before starting, why use Laravel Octane?

Laravel Octane is a tool that allows us to access some functionality and features exposed by the two application servers we just mentioned.

One of the benefits of Laravel Octane is the huge improvement in the response times to HTTP requests by clients such as web browsers. When we develop a Laravel application, we use an important software layer implemented by the framework. This software layer needs time and resources to start. Even if we talk about only a few resources and a short amount of time, this repeated action for each request, especially in a context where there are many requests, can be a problem. Or rather, its optimization could bring enormous benefits.

Laravel Octane, through application servers, does just that: optimizes the process of starting the framework, which typically happens for each individual request. We will see in detail how this is done; essentially, the objects and everything that the framework needs are started and allocated to the start of the application server, and then the instances are made available to the various workers. Workers are the processes that are initiated to serve the requests.

Another reason why it is interesting to evaluate the adoption of Laravel Octane in your Laravel web application is that by using an application server such as Swoole, you can access those features implemented by Swoole.

The functions are, for example, the advanced mechanisms of the cache driver, shared storage for sharing information between the various workers, and the execution of tasks in parallel.

This is a totally new concept to the classic PHP developer who typically does not have an immediate functionality available in the PHP core for the parallelization of processes.

This chapter will introduce you to the Laravel ecosystem and explore what Laravel Octane is.

This chapter aims to introduce you to the application server approach, in which more workers cooperate to manage multiple requests. Understanding the behavior under the hood allows the developer to avoid some mistakes, especially on shared resources (objects and global states) across the worker. This is important because the classical PHP approach is to have one dedicated thread to manage one request.

In this chapter, we will cover the following topics:

  • Exploring the Laravel ecosystem
  • Understanding the request lifecycle
  • Getting to know the application server
 

Technical requirements

In order to run the code and tools shown in this book, you must have the PHP engine installed on your machine. It is recommended that you have a recent version of PHP installed (at least 8.0, released in November 2020).

Also, in order to easily install additional tools, it is recommended that you have Homebrew installed if you use macOS. In the case of GNU/Linux systems, it will be sufficient to resort to using the package manager of the distribution used, and in the case of Windows systems, the advice is to have a virtual environment, for example, with Docker.

In the current chapter, some commands and source code will be shown, simply to share some concepts. In subsequent chapters, especially the second chapter about RoadRunner and the third chapter about Open Swoole, the installation of each package and tool will be addressed step by step.

There are those who, regardless of their operating system, prefer to maintain a “clean” installation by resorting to using Docker regardless of the host operating system. Where the next chapters deal with the installation of operating system-dependent tools, the different methods will be highlighted depending on the system in use.

The source code and the configuration files of the examples described in the current chapter are available here: https://github.com/PacktPublishing/High-Performance-with-Laravel-Octane/tree/main/octane-ch01

 

Exploring the Laravel ecosystem

Laravel is a great framework in the PHP ecosystem that helps developers to build web applications quickly and reliably.

It includes, as dependencies, some great tools from the PHP ecosystem, such as Symfony packages, and some other solid and mature packages such as Monolog for logging, Flysystem for accessing files and storage, and CommonMark for managing Markdown format.

From the Symfony world, Laravel includes packages such as Symfony/routing to manage routing, and http-foundation and http-kernel to manage HTTP communication.

All this is just to say that Laravel uses the best parts of the PHP ecosystem, puts them together, and provides tools, helpers, classes, and methods to simplify the usage of all the tools from the developer’s perspective.

In addition, Laravel is more than a framework. Laravel is an ecosystem.

Laravel also provides applications and services that are integrated with the framework.

For example, Laravel provides the following:

  • Cashier: For integration with Stripe and Paddle for the payment and subscription process.
  • Breeze, Jetstream, Sanctum, and Socialite: For managing authorization, authentication, the social login integration process, and exposing protected APIs.
  • Dusk and Pest: For testing.
  • Echo: For broadcasting events in real time.
  • Envoyer, Forge, and Vapor: For server or serverless management and to manage the deployment process.
  • Mix: For compiling JavaScript and CSS through a webpack configuration fully integrated with the Laravel frontend.
  • Horizon: A web user interface for monitoring queues based on Redis.
  • Nova: An administrator panel builder for Laravel applications.
  • Sail: A local development environment based on Docker.
  • Scout: A full-text search engine, backed by providers such as Algolia, Meilisearch, or simply by the MySQL or PostgreSQL database.
  • Spark: A boilerplate solution for managing billing/subscription in your application.
  • Telescope: UI module for showing debugging and insights.
  • Valet: A macOS-specific bundle of applications configured for running the PHP application. It has dependencies with nginx, PHP, and Dnsmasq.
  • Octane: For improving performance and optimizing resources.

In this book, we will analyze the last tool in this list: Laravel Octane.

We will go over the use of other tools within the Laravel ecosystem, such as Sail (for simplifying the installation process of a complete development environment), and Valet (for correctly setting up the local environment to run a web server and PHP). Also, Laravel Octane depends on important software that we will see in-depth throughout the book. Laravel Octane has strong requirements: it requires additional software such as Swoole or RoadRunner.

But one step at a time.

Before we delve into the tools and their configuration, it’s important to understand some basic mechanisms for managing HTTP requests.

HTTP

HTTP is a protocol that defines rules, messages, and methods for fetching resources on the web, such as HTML documents (web pages) and assets. Clients (who require the resource) and servers (who serve the resource) communicate by exchanging messages. The client sends requests, and the server sends responses.

One of the goals of the book is to empower you to improve the performance of your web applications by doing different things, starting with designing the architecture of the application, choosing and using the right tools, writing code, and finally, releasing the application.

The tools we are going to analyze and use will do much of the work, but I think it is important to understand the underlying dynamics to have a good awareness of how the various tools work to enable you to configure, integrate, and use them to the best of their ability.

Before we get deeper into the workings of Laravel Octane, let me take you through how servers typically deal with HTTP requests by explaining the HTTP request lifecycle.

 

Understanding the HTTP request lifecycle

There are a number of components involved in performing the HTTP request. The components are as follows:

  • Client: This is where the request starts and the response ends (for example, the browser).
  • Network: The requests and responses go through this and it connects the server and the client.
  • Proxy: This is an optional component that could perform some tasks before the request reaches the web server, such as caching, rewriting and/or altering the request, and forwarding the request to the right web server.
  • Web server: This receives the request and is responsible for selecting the correct resource.
  • PHP: The language, or more generally in the case of server-side languages, the language-specific engine that is used and involved. In this case, the PHP interpreter is used. The PHP interpreter can be activated mainly in two ways: as a web server module or as a separate process. In the latter case, a technology called FastCGI Process Manager (FPM) is used. We will see how this mechanism works later in more detail. For now, it is useful to know that the web server somehow invokes the server-side language interpreter. By doing this, our server is able to interpret the language. If the invoked resource is a PHP-type file with the specific PHP syntax, the resource file requested is interpreted by the PHP engine. The output is sent back in the form of a response to the web server, the network, and then the browser.
  • Framework: In the case that the application is written with PHP and a framework is used, as a developer, you can access classes, methods, and helpers to build your application faster.

The components are called sequentially in the HTTP request flow. The HTTP request starts from the browser, then goes through the network (optionally passing through via the proxy), until it reaches the web server that invokes the PHP engine and the framework is bootstrapped.

From a performance point of view, if you want to bring some improvement, you have to take some action or implement some solution depending on the elements of this architecture.

For example, on the browser side, you could work on caching assets in the browser or on the optimization of your JavaScript code. On the networking side, one solution could be resource optimization, for example, reducing the weight of assets or introducing architectural elements such as a CDN. In the case of the web server, an effective first-level improvement could be to avoid loading the PHP engine for the static assets (non-PHP files).

All such fine-tuning will be addressed in the final chapters, where we will deal with the configuration and optimization of production elements. Most of the book covers the optimization of the framework. For example, in Chapters 2 and 3, topics such as the use of Octane with tools such as Swoole and RoadRunner, which enable more efficient and effective loading of resources (shared objects and structures), are addressed. Other points of performance improvement on the framework side include the introduction of an asynchronous approach through the use of queuing systems (Chapters 6 and 7).

Now that you have an idea of the components involved in an HTTP request, let’s look at the structure of an HTTP request.

The structure of an HTTP request

To understand in detail what happens in a typical HTTP request, we start by analyzing what is sent from the browser to the server during a request. A request is mainly characterized by methods (GET, POST, PUT, and so on), the URL, and HTTP headers.

The URL is visible in the browser’s address bar, whereas the headers are handled automatically by the browser and are not directly visible to the user.

The following describes the structure of an HTTP request:

  • The HTTP method (or HTTP verbs) in an HTTP request represents the operation the frontend side wants to perform on the server side with the requested resource:
    • The GET method: Reads and retrieves a resource
    • The POST method: Creates a new resource
    • The PUT method: Replaces a resource
    • The PATCH method: Edits the resource
    • The DELETE method: Deletes the resource
  • The URL identifies the resource. We’ll explain the URL structure in the next section (Handling an HTTP request).
  • The headers include additional information that allows the server to understand how to handle the resource. This information can comprise authentication information, the required format of the resource, and so on.
  • The body payload is additional data, for example, the data sent when a form is submitted to the server.

Now that you have an idea of the structure of an HTTP request, let’s see how such requests are handled.

Handling an HTTP request

A URL is made up of the protocol, the hostname, the port, the path, and the parameters. A typical URL is as follows:

<protocol>://<hostname>:<port>/<path>?<parameters>

For example, a URL could be the following:

https://127.0.0.1:8000/home?cache=12345

Each part that makes up the HTTP request is used specifically by the various software involved in handling the HTTP request:

  • A protocol is used by the browser to determine the communication encryption (encrypted via HTTPS or non-encrypted via HTTP).
  • A hostname is used by the DNS to resolve the hostname into an IP address, and by the web server to involve the right virtual host.
  • A port is used by the operating system of the server to access the right process.
  • A path is used by the web server to call the right resource and for the framework to activate the routing mechanism.
  • Parameters are used by the application to control the behavior of the logic (server-side for query parameters and client-side for the anchor parameters). For example, the query parameters are defined after the ? character, and the anchor parameters are defined after the # character in the URL: https://127.0.0.1:8000/?queryparam=1#anchorparam.

First, the protocol (typically HTTP or HTTPS) is defined. Next, the hostname, which is useful for figuring out which server to contact, is specified. Then, there is a part that is not normally specified, which is the port number; typically, it is port 80 for HTTP and 443 for HTTPS. Also present is the path that identifies the resource we are requesting from the server. Finally, two other optional parts deal with parameters. The first concerns server-side parameters (query string), and the second concerns client-side or JavaScript parameters (parameters with anchors).

In addition to the URL, another characteristic element of the request is the HTTP header, which is very important for the server reached by the request to better understand how to handle the request.

HTTP headers are automatically handled by the browser based on some information and browsing state. Typically, the headers specify the format and other information about the resource; for example, they specify the MIME type, user agent, and so on.

They also specify any access tokens in case the requested resource is protected. The elements to manage the state are also present in the headers as cookies and references for the session. This information is useful for the server to understand and relate consecutive requests.

Why is it so important to understand how a request is composed? Because in analyzing optimization elements regarding performance, the structure of the URL and the parts that make up the headers determine the behavior of different elements within the web architecture (browser, network, web server, server-side language, and framework).

For example, an element such as a hostname is useful to the DNS (network) to be able to resolve the hostname into the IP address. Knowing this is useful in deciding whether to do caching, for example, for name resolution.

Each element involved has its own characteristics that can be optimized to be able to get better performance.

One of the characterizing elements of a typical request to a classic PHP application is that each request is independent of any other request. This means that if your PHP script instantiates an object, this operation is repeated with each request. This has little impact if your script is called only a few times and your script is simple.

Let’s try to think of a scenario in which we have a framework-based application, with the application having to deal with a high load of concurrent requests.

A framework-based application has numerous objects at its disposal, which must be instantiated and configured at startup. In the classic case of PHP, the startup of the framework corresponds to a request.

Laravel Octane, on the other hand, introduces a new way of starting the application.

In a classic Laravel web application, it is sufficient to have a web server (such as nginx) or the internal web server provided by Laravel in the case of development on the developer’s local computer. A classic web server can handle requests without any kind of resource-sharing unless these resources are external resources such as a database or a cache manager.

In contrast to what happens with a classic web server, an application server has the task of starting and managing the executions of multiple workers. Each worker will be able to handle multiple requests by reusing objects and parts of the logic of your application.

This has one benefit, which is that the actual startup of your application and the setting up of the various objects occur on the first request received from the worker and not on each individual request.

HTTP requests and Laravel

From the Laravel application perspective, the parts involved directly in the HTTPS requests are typically routes and controllers.

Handling a request through a Laravel application typically means having to implement the routing part and implement the logic to manage the request in the controller. Routing allows us to define which code to execute within our Laravel application against a specific path in the URL. For example, we might want to define that the code of a method in a specific class such as HomeController::home() must be invoked against a request that has a /home path in the URL. In the classic Laravel definition, we would write something like this in the routes/web.php file:

Route::get('/home', [HomeController::class, 'home'])->name("home");

Now we have to implement the logic to manage the request in the HomeController class (that we have to create) and implement the home method. So, in a new app/Http/Controllers/HomeController.php file, you have to implement the HomeController class extending the basic controller class:

<?php
namespace App\Http\Controllers;
class HomeController extends Controller
{
    public function home(): string
    {
        return "this is the Home page";
    }
}

Now that you have an understanding of how web servers handle requests, let us learn more about the application servers that Laravel Octane integrates with.

 

Getting to know the application server for Laravel Octane

In the PHP ecosystem, we have several application servers.

Laravel Octane, which handles server configuration, startup, and execution, integrates mainly with two of them: Swoole and RoadRunner.

We will deal with the installation, configuration, and use of these two application servers in detail later on.

For now, it is enough for us to know that once the application servers are installed, Laravel Octane will take care of their management. Laravel Octane will also take care of their proper startup via the following command:

php artisan octane:start

The octane:serve command is added when Laravel Octane is installed.

In other words, Laravel Octane has a strong dependency on application servers such as RoadRunner or Swoole.

At startup, Laravel Octane via Swoole or RoadRunner activates some workers, as shown in the following figure:

Figure 1.1: The activation of workers

Figure 1.1: The activation of workers

What are workers?

In Octane, a worker is a process that takes charge of handling the requests associated with it. A worker has the responsibility of starting the framework and initializing framework objects.

This has an extremely positive impact from a performance standpoint. The framework is instantiated on the first request assigned to the worker. The second (and subsequent) requests assigned to that worker reuse the objects already instantiated. The side effect of this is that the worker shares instances of global objects and static variables between requests. This means that different calls to the controller can access the data structures that are shared between requests.

To complicate matters, there is the fact that requests assigned to the same worker share a global state, but different workers are independent and have scope independent of each other. So, we can say that not all requests share the same global state. Requests share a global state when associated with the same worker. Two requests from two different workers share nothing.

In order to minimize the side effect, Laravel Octane has the responsibility of managing the reset of classes/objects owned directly by the framework across the requests.

However, Octane can’t manage and reset classes owned directly by the application.

That’s why the main thing to pay attention to when using Octane is the scope and lifecycle of variables and objects.

To understand this better, I will give you a very basic example.

Example with a shared variable

This example, in the routes/web.php file, creates a route for path / and returns a human-readable timestamp. To simplify the explanation, we are going to write the logic directly into the route file instead of calling and delegating the logic to a controller:

$myStartTime = microtime(true);
Route::get('/', function () use($myStartTime) {
    return DateTime::createFromFormat('U.u', $myStartTime)
    ->format("r (u)");
});

In the routes/web.php routing file (web.php is already stored in the routes directory in the Laravel root folder project), a $myStartTime variable is instantiated and assigned the current time expressed in milliseconds. This variable is then inherited by the route/management function via the use clause.

In the performance of the function associated with route/, the contents of the $myStartTime variable are returned and then displayed.

With the classic behavior of the Laravel application, at each invocation/execution, the variable is regenerated and reinitialized (each time with a new value).

To start the Laravel application in the classic mode, simply run the following:

php artisan serve

Once the web server is started, go to the following URL via the browser: http://127.0.0.1:8000

By continuously reloading the page, a different value is displayed each time; basically, the timestamp is displayed with each request.

Instead of using the development web server provided by Laravel, you would use Laravel Octane and have a different result. At each page refresh (reloading of the web page), you would always see the same value. The value is relative to the timestamp of the first request served. This means that the variable is initialized with the first request and then the value is reused across the requests.

If you try to refresh multiple times, in some cases, you could see a new value.

If this happens, it means that the request was managed by the second (or a new) worker. This means that this behavior is quite unpredictable because Octane acts as a load balancer. When a request comes from the network, the application server will decide which worker (of those available) to assign the request to.

In addition to this, another element that could cause a new value to be generated is when you hit the maximum number of requests managed by a single worker. We will see how to define the maximum number of requests later, and in general, we will have a deep dive session (in Chapters 2 and 3) into Laravel Octane configuration.

The behavior whereby variables are shared across workers until the application server is restarted is valid only for global variables or objects stored in the application service container. The local variables (the variables for which the scope is limited to a function or a method) are not affected.

For example, in the code previously shown, I’m going to declare a $myLocalStartTime variable in the function called by the routing mechanism. The scope of the $myLocalStartTime variable and its lifecycle is limited to the Closure function:

$myStartTime = microtime(true);
Route::get('/', function () use($myStartTime) {
    $myLocalStartTime = microtime(true);
    return DateTime::createFromFormat('U.u', $myStartTime)
    ->format("r (u)") . " - " .
    DateTime::createFromFormat('U.u', $myLocalStartTime)
    ->format("r (u)");
});

Execute the following command with the classic Laravel web server:

php artisan serve

You will see that both values will change on each new request. You can see that when you open a browser to http://127.0.0.1:8000.

Launch Octane as a server with the following command:

php artisan octane:start

You will see, in your browser at http://127.0.0.1:8000, two different dates/times with milliseconds. If you refresh the page, you will see a change in just the second one ($myLocalStartTime).

You have to be aware of this behavior when you are building an application based on Octane.

Another example to better understand this behavior is creating a class with a static property.

Creating a class with a static property

In order to keep this example as simple as possible, I created a MyClass class in the routes/web.php file.

I’m going to add new routes that call the add() method of the MyClass object and then call and return the value of the static property retrieved by the get() method.

In routes/web.php, add the following class:

class MyClass
{
    public static $number = 0;
    public function __construct()
    {
        print "Construct\n";
    }
    public function __destruct()
    {
        print "Deconstruct\n";
    }
    public function add()
    {
        self::$number++;
    }
    public function get()
    {
        return self::$number;
    }
}

Then, in the routes/web.php file, declare the new route as follows:

Route::get('/static-class', function (MyClass $myclass) {
    $myclass->add();
    return $myclass->get();
});

Next, you can launch Laravel in a classic way using the following command:

php artisan serve

Now, if you access the URL http://127.0.0.1:8000/static-class multiple times, the value 1 will be shown. This is because, classically, for every request, the MyClass object is instanced from scratch.

Launch Laravel Octane using the following command:

php artisan octane:serve

If you then access the URL http://127.0.0.1:8000/static-class multiple times, you will see the value 1 in the first request, 2 in the second, 3 in the third, and so on. This is because, with Octane, MyClass is instanced for every request, but the static values are kept in memory.

With a non-static property, we can see the difference as follows:

class MyClass
{
    public static $numberStatic = 0;
    public $number = 0;
    public function __construct()
    {
        print "Construct\n";
    }
    public function __destruct()
    {
        print "Deconstruct\n";
    }
    public function add()
    {
        self::$numberStatic++;
        $this->number++;
    }
    public function get()
    {
        return self::$numberStatic . " - " . $this->number;
    }
}

After calling the page five times, the result shown in the browser will be as follows:

Construct Deconstruct 5 – 1

This is quite simple but, in the end, good for understanding the behavior of static variables under the hood.

The use of static variables is not so unusual. Just think of singleton objects or the app container of Laravel.

To avoid unexpected behavior – as in this specific example with static variables but more generally, with global objects (Laravel makes extensive use of them) – explicit re-initialization must be taken care of. In this case, the static variable is initialized in the constructor. My suggestion is to use explicit initialization of the properties in the constructor. This is because it is the developer’s responsibility to take care of the re-initialization of the variables in the case of global states (objects and variables).

class MyClass
{
    public static $numberStatic = 0;
    public $number = 0;
    public function __construct()
    {
        print "Construct\n";
        self::$numberStatic = 0;
    }
    public function __destruct()
    {
        print "Deconstruct\n";
    }
    public function add()
    {
        self::$numberStatic++;
        $this->number++;
    }
    public function get()
    {
        return self::$numberStatic . " - " . $this->number;
    }
}

We have seen just some very basic examples of the impact on the code if you are going to install and use Laravel Octane. The examples shown earlier were purposely very simple, but with the goal of being easy to understand. In the chapter where we will use Octane in a real scenario, we will cover more realistic examples.

Now we will analyze the impact on performance. So, by installing Octane, what kind of improvement could we have in terms of performance?

 

Understanding performance measurement in Laravel Octane

We have said that introducing Laravel Octane in your application allows for a performance boost, mainly because the objects and the various instances of the classes used by the framework are no longer initialized at every single HTTP request but at the start of the application server. As a result, for each HTTP request, framework objects are reused. Reusing framework objects saves time in serving the HTTP request.

While, on a logical and understandable level, this can have a positive impact in terms of performance, the goal of this part is to get pragmatic feedback on this performance boost by trying to recover some metrics and values.

In order to provide a rough indication of the benefits and improved response time performance for a request, let us try to perform a simple performance test.

To do this, we are going to install a tool to generate and execute some HTTP-concurrent requests. There are several such tools, one of which is wrk (https://github.com/wg/wrk).

If you have a macOS environment, you could use the brew command (provided by Homebrew) to install the wrk tool. To install the tool, use brew install as shown:

brew install wrk

With wrk, you can generate concurrent requests for a defined amount of time.

We will conduct two tests for comparison: one test will be conducted with a classical web application on nginx (http://octane.test), and the other one with an application served by an application server on Laravel Octane (http://octane.test:8000).

The two URLs are resolved as shown:

  • http://octane.test/ is resolved with local address 127.0.0.1 and will reply nginx
  • http://octane.test:8000/ is resolved with local address 127.0.0.1 and port 8000 is bound by Swoole

The wrk execution will use 4 threads, open 20 connections, and take 10 seconds of tests.

So, to test NGINX, launch the wrk command with these arguments:

wrk -t4 -c20 -d10s http://octane.test

You will see the following output:

Running 10s test @ http://octane.test
  4 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    51.78ms   61.33ms 473.05ms   88.54%
    Req/Sec   141.79     68.87   313.00     66.50%
  5612 requests in 10.05s, 8.47MB read
  Non-2xx or 3xx responses: 2
Requests/sec:    558.17
Transfer/sec:    863.14KB

To test Laravel Octane (RoadRunner), use the following command:

wrk -t4 -c20 -d10s http://octane.test:8000

You will see the following output:

Running 10s test @ http://octane.test:8000
  4 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   134.58ms  178.24ms   1.09s    79.75%
    Req/Sec   222.72    192.63     1.02k    73.72%
  7196 requests in 10.02s, 8.06MB read
Requests/sec:    718.51
Transfer/sec:    823.76KB

This test is very basic because there are no special server-side logic or query databases involved, but it is good to run the test to understand the raw difference in bootstrapping basic objects for Laravel (application container, requests, etc.) and perceive their flavor.

The difference is not so great (7,196 requests versus 5,612 requests) – around 22% – but consider that this difference grows if you add new packages and libraries (more code to be bootstrapped for each request).

Consider also that RoadRunner and Swoole provide other additional tools for improving performances such as enabling concurrency and executing concurrent tasks. The additional tools will be shown later in Chapters 2 and 3.

To better explain why Laravel Octane allows you to achieve this improvement, let me demonstrate how and when service providers are instanced and loaded into a service container.

Typically, in a classic Laravel application service, providers are loaded in each request.

Create a new service provider named MyServiceProvider in the app/Providers directory:

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider
{
    public function __construct($app)
    {
        echo "NEW     - " . __METHOD__ . PHP_EOL;
        parent::__construct($app);
    }
    public function register()
    {
        echo "REGISTER     - " . __METHOD__ . PHP_EOL;
    }
    public function boot()
    {
        echo "BOOT     - " . __METHOD__ . PHP_EOL;
    }
}

The new service provider simply shows a message when the service provider is created, registered, and booted.

The lifecycle of a service provider starts with three phases: creation, registration, and boot.

The register() and boot() methods are needed for dependency resolution. First of all, every service provider is registered. Once they are all registered, they could be booted. If a service provider needs another service in the boot method, you can be sure that it is ready to be used because it is already registered.

Then, you have to register the service provider, so in config/app.php in the providers array, add App\Providers\MyServiceProvider::class.

In a classical Laravel web application, for every HTTP request, the MyServiceProvider service provider is instanced, and the construct, register, and boot methods are called every time, showing this output:

NEW          - App\Providers\MyServiceProvider::__construct
REGISTER     - App\Providers\MyServiceProvider::register
BOOT         - App\Providers\MyServiceProvider::boot

With Laravel Octane, something different happens.

For a better understanding, we are going to launch the Laravel Octane server with two parameters:

  • workers: The number of workers that should be available to handle requests. We are going to set this number to 2.
  • max-requests: The number of requests to process before reloading the server. We are going to set this number to a maximum limit of 5 for each worker.

To start the Octane server with two workers and reload the server after processing five requests, we enter the following command:

php artisan octane:start --workers=2 --max-requests=5

After launching Octane, try to perform more than one request with the browser accessing this URL: http://127.0.0.1:8000.

The following is the output:

NEW          - App\Providers\MyServiceProvider::__construct
REGISTER     - App\Providers\MyServiceProvider::register
BOOT         - App\Providers\MyServiceProvider::boot
  200    GET / ...................................................... 113.62 ms
NEW          - App\Providers\MyServiceProvider::__construct
REGISTER     - App\Providers\MyServiceProvider::register
BOOT         - App\Providers\MyServiceProvider::boot
  200    GET / ....................................................... 85.49 ms
  200    GET / ........................................................ 7.57 ms
  200    GET / ........................................................ 6.96 ms
  200    GET / ........................................................ 6.40 ms
  200    GET / ........................................................ 7.27 ms
  200    GET / ........................................................ 3.97 ms
  200    GET / ........................................................ 5.17 ms
  200    GET / ........................................................ 8.41 ms
worker stopped
  200    GET / ........................................................ 4.84 ms
worker stopped

The first 2 requests take around 100 milliseconds (ms), the next requests take 10 ms, and the register() and boot() methods are called on the first two requests.

So we can see the first two requests (two because we have two workers) are a bit slower (113.62 ms and 85.49 ms) than the next requests (from the third to the tenth request, where we have a response time of less than 10 ms).

Another important thing to mention is that the register and boot methods are called for the first two requests until the tenth request (two workers multiplied by five max requests). This behavior is repeated for subsequent requests.

And so, installing Laravel Octane in your web application allows you to improve the response time of your application.

All this without having involved certain tools such as concurrency management provided by application servers such as Swoole and RoadRunner.

 

Summary

Now that we have an overview of the behavior, some benefits, and some side effects of Laravel Octane, we can proceed with the next chapter by installing and configuring one of the two Laravel Octane-compatible application servers: the RoadRunner application server.

We will revisit some of the instructions addressed in this chapter. The goal of this chapter was to provide some useful summary elements to address the more specific and detailed cases in the rest of the book.

About the Author
  • Roberto Butti

    Roberto Butti has been working in Web software development since 2000. He has worked for different markets and companies as well as with several languages/frameworks on both the server- and client-side Web. Today, he is focused on Web architecture with Laravel on the server side and various reactive Javascript frameworks such as React, Vue, and Svelte on the client side. Always passionate about performant and scalable architectures, he found in Laravel Octane, and more generally in OpenSwoole, an enabling tool for projects where concurrency, real-time, and performance are crucial.

    Browse publications by this author
High Performance with Laravel Octane
Unlock this book and the full library FREE for 7 days
Start now