1. Paquetes
  2. Laravel Octane

Introducción

Laravel Octane potencia el rendimiento de tu aplicación al servirla utilizando servidores de aplicaciones potentes, incluidos Open Swoole, Swoole y RoadRunner. Octane inicia tu aplicación una vez, la mantiene en memoria y luego le envía solicitudes a velocidades supersónicas.

Instalación

Octane se puede instalar a través del administrador de paquetes Composer:

composer require laravel/octane

Después de instalar Octane, puedes ejecutar el comando Artisan octane:install, que instalará el archivo de configuración de Octane en tu aplicación:

php artisan octane:install

Requisitos previos del servidor

Advertencia Laravel Octane requiere PHP 8.1+.

RoadRunner

RoadRunner está alimentado por el binario RoadRunner, que está construido con Go. La primera vez que inicias un servidor Octane basado en RoadRunner, Octane te ofrecerá descargar e instalar el binario RoadRunner por ti.

RoadRunner a través de Laravel Sail

Si planeas desarrollar tu aplicación utilizando Laravel Sail, debes ejecutar los siguientes comandos para instalar Octane y RoadRunner:

./vendor/bin/sail up
 
./vendor/bin/sail composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http

A continuación, debes iniciar un shell de Sail y utilizar el ejecutable rr para obtener la última versión del binario RoadRunner basado en Linux:

./vendor/bin/sail shell
 
# Within the Sail shell...
./vendor/bin/rr get-binary

Después de instalar el binario RoadRunner, puedes salir de tu sesión de shell de Sail. Ahora deberás ajustar el archivo supervisor.conf utilizado por Sail para mantener tu aplicación en funcionamiento. Para empezar, ejecuta el comando Artisan sail:publish:

./vendor/bin/sail artisan sail:publish

A continuación, actualiza la directiva command del archivo docker/supervisord.conf de tu aplicación para que Sail sirva tu aplicación utilizando Octane en lugar del servidor de desarrollo PHP:

command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=80

Finalmente, asegúrate de que el binario rr sea ejecutable y construye las imágenes de Sail:

chmod +x ./rr
 
./vendor/bin/sail build --no-cache

Swoole

Si planeas utilizar el servidor de aplicaciones Swoole para servir tu aplicación Laravel Octane, debes instalar la extensión de PHP Swoole. Por lo general, esto se puede hacer a través de PECL:

pecl install swoole

Open Swoole

Si deseas utilizar el servidor de aplicaciones Open Swoole para servir tu aplicación Laravel Octane, debes instalar la extensión de PHP Open Swoole. Por lo general, esto se puede hacer a través de PECL:

pecl install openswoole

El uso de Laravel Octane con Open Swoole proporciona la misma funcionalidad que Swoole, como tareas concurrentes, ticks e intervalos.

Swoole a través de Laravel Sail

Advertencia Antes de servir una aplicación Octane a través de Sail, asegúrate de tener la última versión de Laravel Sail y ejecuta ./vendor/bin/sail build --no-cache dentro del directorio raíz de tu aplicación.

Alternativamente, puedes desarrollar tu aplicación Octane basada en Swoole utilizando Laravel Sail, el entorno de desarrollo oficial basado en Docker para Laravel. Laravel Sail incluye la extensión Swoole por defecto. Sin embargo, aún deberás ajustar el archivo supervisor.conf utilizado por Sail para mantener en ejecución tu aplicación. Para empezar, ejecuta el comando Artisan sail:publish:

./vendor/bin/sail artisan sail:publish

A continuación, actualiza la directiva command en el archivo docker/supervisord.conf de tu aplicación para que Sail sirva tu aplicación utilizando Octane en lugar del servidor de desarrollo de PHP:

command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port=80

Finalmente, construye tus imágenes de Sail:

./vendor/bin/sail build --no-cache

Configuración de Swoole

Swoole admite algunas opciones de configuración adicionales que puedes agregar a tu archivo de configuración octane si es necesario. Dado que rara vez es necesario modificarlas, estas opciones no se incluyen en el archivo de configuración predeterminado:

'swoole' => [
'options' => [
'log_file' => storage_path('logs/swoole_http.log'),
'package_max_length' => 10 * 1024 * 1024,
],
],

Servir su aplicación

El servidor Octane se puede iniciar mediante el comando Artisan octane:start. Por defecto, este comando utilizará el servidor especificado por la opción de configuración server en el archivo octane de tu aplicación:

php artisan octane:start

Por defecto, Octane iniciará el servidor en el puerto 8000, por lo que podrás acceder a tu aplicación en un navegador web a través de http://localhost:8000.

Servir su aplicación a través de HTTPS

Por defecto, las aplicaciones que se ejecutan a través de Octane generan enlaces con el prefijo http://. La variable de entorno OCTANE_HTTPS, utilizada en el archivo de configuración config/octane.php de tu aplicación, puede establecerse en true cuando sirvas tu aplicación a través de HTTPS. Cuando este valor de configuración se establece en true, Octane indicará a Laravel que prefije todos los enlaces generados con https://:

'https' => env('OCTANE_HTTPS', false),

Servir su aplicación a través de Nginx

Nota Si aún no estás listo para gestionar la configuración de tu propio servidor o no te sientes cómodo configurando todos los diversos servicios necesarios para ejecutar una aplicación robusta de Laravel Octane, consulta Laravel Forge.

En entornos de producción, debes servir tu aplicación Octane detrás de un servidor web tradicional como Nginx o Apache. De esta manera, el servidor web podrá servir tus activos estáticos, como imágenes y hojas de estilo, además de gestionar la terminación del certificado SSL.

En el ejemplo de configuración de Nginx a continuación, Nginx servirá los activos estáticos del sitio y dirigirá las solicitudes al servidor Octane que se ejecuta en el puerto 8000:

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
 
server {
listen 80;
listen [::]:80;
server_name domain.com;
server_tokens off;
root /home/forge/domain.com/public;
 
index index.php;
 
charset utf-8;
 
location /index.php {
try_files /not_exists @octane;
}
 
location / {
try_files $uri $uri/ @octane;
}
 
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
 
access_log off;
error_log /var/log/nginx/domain.com-error.log error;
 
error_page 404 /index.php;
 
location @octane {
set $suffix "";
 
if ($uri = /index.php) {
set $suffix ?$query_string;
}
 
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Scheme $scheme;
proxy_set_header SERVER_PORT $server_port;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
 
proxy_pass http://127.0.0.1:8000$suffix;
}
}

Observar cambios en archivos

Dado que tu aplicación se carga en memoria una vez cuando se inicia el servidor Octane, cualquier cambio en los archivos de tu aplicación no se reflejará al actualizar el navegador. Por ejemplo, las definiciones de ruta agregadas a tu archivo routes/web.php no se reflejarán hasta que se reinicie el servidor. Para mayor comodidad, puedes utilizar la bandera --watch para indicarle a Octane que reinicie automáticamente el servidor ante cualquier cambio de archivos en tu aplicación:

php artisan octane:start --watch

Antes de utilizar esta función, asegúrate de que Node esté instalado en tu entorno de desarrollo local. Además, debes instalar la biblioteca de observación de archivos Chokidar en tu proyecto:

npm install --save-dev chokidar

Puedes configurar los directorios y archivos que se deben observar mediante la opción de configuración watch en el archivo de configuración config/octane.php de tu aplicación.

Especificar la cantidad de trabajadores

Por defecto, Octane iniciará un trabajador de solicitud de aplicación por cada núcleo de CPU proporcionado por tu máquina. Estos trabajadores se utilizarán para atender las solicitudes HTTP entrantes a medida que ingresan a tu aplicación. Puedes especificar manualmente cuántos trabajadores deseas iniciar utilizando la opción --workers al invocar el comando octane:start:

php artisan octane:start --workers=4

Si estás utilizando el servidor de aplicaciones Swoole, también puedes especificar cuántos "trabajadores de tareas" deseas iniciar:

php artisan octane:start --workers=4 --task-workers=6

Especificar el recuento máximo de solicitudes

Para ayudar a prevenir posibles fugas de memoria, Octane reinicia graciosamente cualquier trabajador una vez que ha manejado 500 solicitudes. Para ajustar este número, puedes utilizar la opción --max-requests:

php artisan octane:start --max-requests=250

Recargar los trabajadores

Puedes reiniciar graciosamente los trabajadores de aplicación del servidor Octane utilizando el comando octane:reload. Normalmente, esto se debe hacer después de implementar para que tu código recién implementado se cargue en memoria y se utilice para atender las solicitudes posteriores:

php artisan octane:reload

Detener el servidor

Puedes detener el servidor Octane utilizando el comando Artisan octane:stop:

php artisan octane:stop

Verificar el estado del servidor

Puedes verificar el estado actual del servidor Octane utilizando el comando Artisan octane:status:

php artisan octane:status

Inyección de dependencias y Octane

Dado que Octane inicia tu aplicación una vez y la mantiene en memoria mientras atiende solicitudes, hay algunas advertencias que debes tener en cuenta al construir tu aplicación. Por ejemplo, los métodos register y boot de los proveedores de servicios de tu aplicación solo se ejecutarán una vez cuando el trabajador de solicitudes se inicie inicialmente. En solicitudes subsiguientes, se reutilizará la misma instancia de aplicación.

En este sentido, debes tener cuidado al inyectar el contenedor de servicios de la aplicación o la solicitud en el constructor de cualquier objeto. Al hacerlo, ese objeto puede tener una versión obsoleta del contenedor o de la solicitud en solicitudes subsiguientes.

Octane manejará automáticamente la restauración de cualquier estado del framework de primera parte entre solicitudes. Sin embargo, Octane no siempre sabe cómo restablecer el estado global creado por tu aplicación. Por lo tanto, debes ser consciente de cómo construir tu aplicación de una manera que sea amigable con Octane. A continuación, discutiremos las situaciones más comunes que pueden causar problemas al usar Octane.

Inyección de contenedor

En general, debes evitar inyectar el contenedor de servicios de la aplicación o la instancia de solicitud HTTP en los constructores de otros objetos. Por ejemplo, la siguiente vinculación inyecta el contenedor de servicios completo en un objeto que se vincula como un singleton:

use App\Service;
use Illuminate\Contracts\Foundation\Application;
 
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
$this->app->singleton(Service::class, function (Application $app) {
return new Service($app);
});
}

En este ejemplo, si la instancia de Service se resuelve durante el proceso de inicio de la aplicación, el contenedor se inyectará en el servicio y esa misma instancia del contenedor se mantendrá en la instancia de Service en solicitudes subsiguientes. Esto puede no ser un problema para tu aplicación en particular; sin embargo, puede llevar a que el contenedor no tenga inyecciones que se agregaron más adelante en el ciclo de inicio o por una solicitud subsiguiente.

Como solución temporal, podrías dejar de registrar la vinculación como un singleton o podrías inyectar un cierre de resolución del contenedor en el servicio que siempre resuelva la instancia actual del contenedor:

use App\Service;
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->bind(Service::class, function (Application $app) {
return new Service($app);
});
 
$this->app->singleton(Service::class, function () {
return new Service(fn () => Container::getInstance());
});

El ayudante global app y el método Container::getInstance() siempre devolverán la versión más reciente del contenedor de la aplicación.

Inyección de solicitud

En general, debes evitar inyectar el contenedor de servicios de la aplicación o la instancia de solicitud HTTP en los constructores de otros objetos. Por ejemplo, la siguiente vinculación inyecta la instancia completa de la solicitud en un objeto que se vincula como un singleton:

use App\Service;
use Illuminate\Contracts\Foundation\Application;
 
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
$this->app->singleton(Service::class, function (Application $app) {
return new Service($app['request']);
});
}

En este ejemplo, si la instancia de Service se resuelve durante el proceso de inicio de la aplicación, la solicitud HTTP se inyectará en el servicio y esa misma solicitud se mantendrá en la instancia de Service en solicitudes subsiguientes. Por lo tanto, todas las cabeceras, datos de entrada y cadena de consulta serán incorrectos, así como todos los demás datos de la solicitud.

Como solución temporal, podrías dejar de registrar la vinculación como un singleton, o podrías inyectar un cierre de resolución de solicitud en el servicio que siempre resuelva la instancia actual de la solicitud. O, el enfoque más recomendado es simplemente pasar la información específica de la solicitud que tu objeto necesita a uno de los métodos del objeto en tiempo de ejecución:

use App\Service;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->bind(Service::class, function (Application $app) {
return new Service($app['request']);
});
 
$this->app->singleton(Service::class, function (Application $app) {
return new Service(fn () => $app['request']);
});
 
// Or...
 
$service->method($request->input('name'));

El ayudante global request siempre devolverá la solicitud que la aplicación está manejando actualmente y, por lo tanto, es seguro usarlo dentro de tu aplicación.

Advertencia Es aceptable hacer una sugerencia de tipo de instancia Illuminate\Http\Request en los métodos de controlador y cierres de rutas.

Inyección del repositorio de configuración

En general, debes evitar inyectar la instancia del repositorio de configuración en los constructores de otros objetos. Por ejemplo, la siguiente vinculación inyecta el repositorio de configuración en un objeto que se vincula como un singleton:

use App\Service;
use Illuminate\Contracts\Foundation\Application;
 
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
$this->app->singleton(Service::class, function (Application $app) {
return new Service($app->make('config'));
});
}

En este ejemplo, si los valores de configuración cambian entre solicitudes, ese servicio no tendrá acceso a los nuevos valores porque depende de la instancia original del repositorio.

Como solución temporal, podrías dejar de registrar la vinculación como un singleton, o podrías inyectar un cierre de resolución de repositorio de configuración en la clase:

use App\Service;
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->bind(Service::class, function (Application $app) {
return new Service($app->make('config'));
});
 
$this->app->singleton(Service::class, function () {
return new Service(fn () => Container::getInstance()->make('config'));
});

El global config siempre devolverá la última versión del repositorio de configuración y, por lo tanto, es seguro usarlo dentro de tu aplicación.

Gestión de fugas de memoria

Recuerda, Octane mantiene tu aplicación en memoria entre solicitudes; por lo tanto, agregar datos a una matriz mantenida estáticamente resultará en una fuga de memoria. Por ejemplo, el siguiente controlador tiene una fuga de memoria ya que cada solicitud a la aplicación seguirá agregando datos a la matriz estática $data:

use App\Service;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
 
/**
* Manejar una solicitud entrante.
*/
public function index(Request $request): array
{
Service::$data[] = Str::random(10);
 
return [
// ...
];
}

Al construir tu aplicación, debes tener especial cuidado para evitar la creación de estos tipos de fugas de memoria. Se recomienda que supervises el uso de memoria de tu aplicación durante el desarrollo local para asegurarte de que no estás introduciendo nuevas fugas de memoria en tu aplicación.

Tareas concurrentes

Advertencia Esta característica requiere Swoole.

Al usar Swoole, puedes ejecutar operaciones de forma concurrente a través de tareas de fondo livianas. Puedes lograr esto utilizando el método concurrently de Octane. Puedes combinar este método con la destrucción de matrices de PHP para recuperar los resultados de cada operación:

use App\Models\User;
use App\Models\Server;
use Laravel\Octane\Facades\Octane;
 
[$users, $servers] = Octane::concurrently([
fn () => User::all(),
fn () => Server::all(),
]);

Las tareas concurrentes procesadas por Octane utilizan los "trabajadores de tareas" de Swoole y se ejecutan en un proceso completamente diferente al de la solicitud entrante. La cantidad de trabajadores disponibles para procesar tareas concurrentes se determina por la directiva --task-workers en el comando octane:start:

php artisan octane:start --workers=4 --task-workers=6

Al invocar el método concurrently, no debes proporcionar más de 1024 tareas debido a las limitaciones impuestas por el sistema de tareas de Swoole.

Ticks e intervalos

Advertencia Esta característica requiere Swoole.

Al usar Swoole, puedes registrar operaciones "tick" que se ejecutarán cada número especificado de segundos. Puedes registrar devoluciones de llamada "tick" mediante el método tick. El primer argumento proporcionado al método tick debe ser una cadena que represente el nombre del temporizador. El segundo argumento debe ser un callable que se invocará en el intervalo especificado.

En este ejemplo, registraremos un cierre que se invocará cada 10 segundos. Normalmente, el método tick debería llamarse dentro del método boot de uno de los proveedores de servicios de tu aplicación:

Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10);

Usando el método immediate, puedes indicar a Octane que invoque inmediatamente la devolución de llamada de tick cuando el servidor Octane se inicie inicialmente y cada N segundos después:

Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10)
->immediate();

La caché de Octane

Advertencia Esta característica requiere Swoole.

Al usar Swoole, puedes aprovechar el controlador de caché de Octane, que proporciona velocidades de lectura y escritura de hasta 2 millones de operaciones por segundo. Por lo tanto, este controlador de caché es una excelente opción para aplicaciones que necesitan velocidades de lectura / escritura extremas desde su capa de almacenamiento en caché.

Este controlador de caché está alimentado por las tablas Swoole. Todos los datos almacenados en la caché están disponibles para todos los trabajadores en el servidor. Sin embargo, los datos en caché se eliminarán cuando se reinicie el servidor:

Cache::store('octane')->put('framework', 'Laravel', 30);

Nota El número máximo de entradas permitidas en la caché de Octane puede definirse en el archivo de configuración octane de tu aplicación.

Intervalos de caché

Además de los métodos típicos proporcionados por el sistema de caché de Laravel, el controlador de caché de Octane cuenta con cachés basadas en intervalos. Estas cachés se actualizan automáticamente en el intervalo especificado y deben registrarse dentro del método boot de uno de los proveedores de servicios de tu aplicación. Por ejemplo, la siguiente caché se actualizará cada cinco segundos:

use Illuminate\Support\Str;
 
Cache::store('octane')->interval('random', function () {
return Str::random(10);
}, seconds: 5);

Tablas

Advertencia Esta característica requiere Swoole.

Al usar Swoole, puedes definir e interactuar con tus propias tablas Swoole arbitrarias. Las tablas Swoole proporcionan un rendimiento extremadamente alto y los datos en estas tablas pueden ser accesibles por todos los trabajadores en el servidor. Sin embargo, los datos dentro de ellas se perderán cuando se reinicie el servidor.

Las tablas deben definirse dentro de la matriz de configuración tables del archivo de configuración octane de tu aplicación. Ya se ha configurado un ejemplo de tabla que permite un máximo de 1000 filas. El tamaño máximo de las columnas de cadena se puede configurar especificando el tamaño de la columna después del tipo de columna, como se ve a continuación:

'tables' => [
'example:1000' => [
'name' => 'string:1000',
'votes' => 'int',
],
],

Para acceder a una tabla, puedes utilizar el método Octane::table:

use Laravel\Octane\Facades\Octane;
 
Octane::table('example')->set('uuid', [
'name' => 'Nuno Maduro',
'votes' => 1000,
]);
 
return Octane::table('example')->get('uuid');

Advertencia Los tipos de columna admitidos por las tablas Swoole son: string, int y float.