- Introducción
- Instalación
- Requisitos del servidor
- Sirviendo su aplicación
- Inyección de dependencias y Octane
- Manejo de fugas de memoria
- Tareas concurrentes
- Ticks e intervalos
- La caché de Octane
- Tablas
#Introducción
Laravel Octane potencia el rendimiento de su aplicación al servirla usando servidores de aplicaciones de alto rendimiento, incluyendo FrankenPHP, Open Swoole, Swoole y RoadRunner. Octane inicia su aplicación una vez, la mantiene en memoria y luego atiende las solicitudes a velocidades supersónicas.
#Instalación
Octane puede instalarse mediante el gestor de paquetes Composer:
composer require laravel/octane
Después de instalar Octane, puede ejecutar el comando Artisan octane:install, que instalará el archivo de configuración de Octane en su aplicación:
php artisan octane:install
#Requisitos del servidor
Laravel Octane requiere PHP 8.1+.
#FrankenPHP
La integración de Octane con FrankenPHP está en beta y debe usarse con precaución en producción.
FrankenPHP es un servidor de aplicaciones PHP, escrito en Go, que soporta características web modernas como early hints y compresión Zstandard. Cuando instala Octane y elige FrankenPHP como servidor, Octane descargará e instalará automáticamente el binario de FrankenPHP por usted.
#FrankenPHP vía Laravel Sail
Si planea desarrollar su aplicación usando Laravel Sail, debe ejecutar los siguientes comandos para instalar Octane y FrankenPHP:
./vendor/bin/sail up
./vendor/bin/sail composer require laravel/octane
Luego, debe usar el comando Artisan octane:install para instalar el binario de FrankenPHP:
./vendor/bin/sail artisan octane:install --server=frankenphp
Finalmente, agregue una variable de entorno SUPERVISOR_PHP_COMMAND a la definición del servicio laravel.test en el archivo docker-compose.yml de su aplicación. Esta variable contendrá el comando que Sail usará para servir su aplicación usando Octane en lugar del servidor de desarrollo PHP:
services:
laravel.test:
environment:
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port=80"
XDG_CONFIG_HOME: /var/www/html/config
XDG_DATA_HOME: /var/www/html/data
Para habilitar HTTPS, HTTP/2 y HTTP/3, aplique estas modificaciones en su lugar:
services:
laravel.test:
ports:
- '${APP_PORT:-80}:80'
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
- '443:443'
- '443:443/udp'
environment:
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --host=localhost --port=443 --admin-port=2019 --https"
XDG_CONFIG_HOME: /var/www/html/config
XDG_DATA_HOME: /var/www/html/data
Normalmente, debería acceder a su aplicación FrankenPHP Sail vía https://localhost, ya que usar https://127.0.0.1 requiere configuración adicional y está desaconsejado.
#FrankenPHP vía Docker
Usar las imágenes oficiales de Docker de FrankenPHP puede ofrecer mejor rendimiento y el uso de extensiones adicionales no incluidas en instalaciones estáticas de FrankenPHP. Además, las imágenes oficiales soportan ejecutar FrankenPHP en plataformas que no soporta nativamente, como Windows. Las imágenes oficiales son adecuadas tanto para desarrollo local como para producción.
Puede usar el siguiente Dockerfile como punto de partida para contenerizar su aplicación Laravel potenciada por FrankenPHP:
FROM dunglas/frankenphp
RUN install-php-extensions \
pcntl
# Añadir otras extensiones PHP aquí...
COPY . /app
ENTRYPOINT ["php", "artisan", "octane:frankenphp"]
Luego, durante el desarrollo, puede utilizar el siguiente archivo Docker Compose para ejecutar su aplicación:
# compose.yaml
services:
frankenphp:
build:
context: .
entrypoint: php artisan octane:frankenphp --max-requests=1
ports:
- "8000:8000"
volumes:
- .:/app
Puede consultar la documentación oficial de FrankenPHP para más información sobre cómo ejecutar FrankenPHP con Docker.
#RoadRunner
RoadRunner es impulsado por el binario RoadRunner, construido con Go. La primera vez que inicia un servidor Octane basado en RoadRunner, Octane le ofrecerá descargar e instalar el binario RoadRunner por usted.
#RoadRunner vía Laravel Sail
Si planea desarrollar su aplicación usando Laravel Sail, debe 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
Luego, debe iniciar un shell de Sail y usar el ejecutable rr para obtener la última versión para Linux del binario RoadRunner:
./vendor/bin/sail shell
# Dentro del shell de Sail...
./vendor/bin/rr get-binary
Luego, agregue una variable de entorno SUPERVISOR_PHP_COMMAND a la definición del servicio laravel.test en el archivo docker-compose.yml de su aplicación. Esta variable contendrá el comando que Sail usará para servir su aplicación usando Octane en lugar del servidor de desarrollo PHP:
services:
laravel.test:
environment:
SUPERVISOR_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úrese de que el binario rr sea ejecutable y construya sus imágenes Sail:
chmod +x ./rr
./vendor/bin/sail build --no-cache
#Swoole
Si planea usar el servidor de aplicaciones Swoole para servir su aplicación Laravel Octane, debe instalar la extensión PHP Swoole. Normalmente, esto puede hacerse vía PECL:
pecl install swoole
#Open Swoole
Si desea usar el servidor de aplicaciones Open Swoole para servir su aplicación Laravel Octane, debe instalar la extensión PHP Open Swoole. Normalmente, esto puede hacerse vía PECL:
pecl install openswoole
Usar Laravel Octane con Open Swoole ofrece la misma funcionalidad que Swoole, como tareas concurrentes, ticks e intervalos.
#Swoole vía Laravel Sail
Antes de servir una aplicación Octane vía Sail, asegúrese de tener la última versión de Laravel Sail y ejecute ./vendor/bin/sail build --no-cache dentro del directorio raíz de su aplicación.
Alternativamente, puede desarrollar su aplicación Octane basada en Swoole usando Laravel Sail, el entorno oficial de desarrollo basado en Docker para Laravel. Laravel Sail incluye la extensión Swoole por defecto. Sin embargo, aún necesitará ajustar el archivo docker-compose.yml usado por Sail.
Para comenzar, agregue una variable de entorno SUPERVISOR_PHP_COMMAND a la definición del servicio laravel.test en el archivo docker-compose.yml de su aplicación. Esta variable contendrá el comando que Sail usará para servir su aplicación usando Octane en lugar del servidor de desarrollo PHP:
services:
laravel.test:
environment:
SUPERVISOR_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, construya sus imágenes Sail:
./vendor/bin/sail build --no-cache
#Configuración de Swoole
Swoole soporta algunas opciones de configuración adicionales que puede agregar a su archivo de configuración octane si es necesario. Como rara vez necesitan modificarse, estas opciones no están incluidas en el archivo de configuración por defecto:
'swoole' => [
'options' => [
'log_file' => storage_path('logs/swoole_http.log'),
'package_max_length' => 10 * 1024 * 1024,
],
],
#Sirviendo su aplicación
El servidor Octane puede iniciarse mediante el comando Artisan octane:start. Por defecto, este comando usará el servidor especificado en la opción server del archivo de configuración octane de su aplicación:
php artisan octane:start
Por defecto, Octane iniciará el servidor en el puerto 8000, por lo que puede acceder a su aplicación en un navegador web vía http://localhost:8000.
#Sirviendo su aplicación vía HTTPS
Por defecto, las aplicaciones que corren vía Octane generan enlaces con el prefijo http://. La variable de entorno OCTANE_HTTPS, usada en el archivo de configuración config/octane.php de su aplicación, puede establecerse en true cuando sirva su aplicación vía HTTPS. Cuando este valor está en true, Octane indicará a Laravel que prefije todos los enlaces generados con https://:
'https' => env('OCTANE_HTTPS', false),
#Sirviendo su aplicación vía Nginx
Si no está listo para gestionar su propia configuración de servidor o no se siente cómodo configurando todos los servicios necesarios para ejecutar una aplicación Laravel Octane robusta, consulte Laravel Forge.
En entornos de producción, debe servir su aplicación Octane detrás de un servidor web tradicional como Nginx o Apache. Esto permitirá que el servidor web sirva sus activos estáticos como imágenes y hojas de estilo, además de gestionar la terminación de su certificado SSL.
En el ejemplo de configuración de Nginx a continuación, Nginx servirá los activos estáticos del sitio y hará proxy de las solicitudes al servidor Octane que corre 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;
}
}
#Vigilando cambios en archivos
Dado que su aplicación se carga en memoria una vez cuando el servidor Octane inicia, cualquier cambio en los archivos de su aplicación no se reflejará al actualizar el navegador. Por ejemplo, definiciones de rutas añadidas a su archivo routes/web.php no se reflejarán hasta que el servidor se reinicie. Para mayor comodidad, puede usar la bandera --watch para indicar a Octane que reinicie automáticamente el servidor ante cualquier cambio en los archivos de su aplicación:
php artisan octane:start --watch
Antes de usar esta función, debe asegurarse de que Node esté instalado en su entorno de desarrollo local. Además, debe instalar la librería de vigilancia de archivos Chokidar en su proyecto:
npm install --save-dev chokidar
Puede configurar los directorios y archivos que deben ser vigilados usando la opción watch en el archivo de configuración config/octane.php de su aplicación.
#Especificando la cantidad de workers
Por defecto, Octane iniciará un worker para solicitudes de aplicación por cada núcleo de CPU disponible en su máquina. Estos workers serán usados para atender las solicitudes HTTP entrantes. Puede especificar manualmente cuántos workers desea iniciar usando la opción --workers al invocar el comando octane:start:
php artisan octane:start --workers=4
Si usa el servidor de aplicaciones Swoole, también puede especificar cuántos "task workers" desea iniciar:
php artisan octane:start --workers=4 --task-workers=6
#Especificando el máximo de solicitudes
Para ayudar a prevenir fugas de memoria, Octane reinicia de forma controlada cualquier worker una vez que ha manejado 500 solicitudes. Para ajustar este número, puede usar la opción --max-requests:
php artisan octane:start --max-requests=250
#Recargando los workers
Puede reiniciar de forma controlada los workers de la aplicación del servidor Octane usando el comando octane:reload. Normalmente, esto debe hacerse después de un despliegue para que el código recién desplegado se cargue en memoria y se use para atender las solicitudes siguientes:
php artisan octane:reload
#Deteniendo el servidor
Puede detener el servidor Octane usando el comando Artisan octane:stop:
php artisan octane:stop
#Verificando el estado del servidor
Puede verificar el estado actual del servidor Octane usando el comando Artisan octane:status:
php artisan octane:status
#Inyección de dependencias y Octane
Dado que Octane inicia su aplicación una vez y la mantiene en memoria mientras atiende solicitudes, hay algunas consideraciones que debe tener en cuenta al construir su aplicación. Por ejemplo, los métodos register y boot de los service providers de su aplicación solo se ejecutarán una vez cuando el worker de solicitudes se inicie inicialmente. En solicitudes posteriores, se reutilizará la misma instancia de la aplicación.
Por ello, debe tener especial cuidado al inyectar el contenedor de servicios de la aplicación o la request en el constructor de cualquier objeto. Al hacerlo, ese objeto podría tener una versión obsoleta del contenedor o la request en solicitudes posteriores.
Octane manejará automáticamente el reinicio de cualquier estado interno del framework entre solicitudes. Sin embargo, Octane no siempre sabe cómo reiniciar el estado global creado por su aplicación. Por lo tanto, debe ser consciente de cómo construir su aplicación para que sea compatible con Octane. A continuación, discutiremos las situaciones más comunes que pueden causar problemas al usar Octane.
#Inyección del contenedor
En general, debe evitar inyectar el contenedor de servicios de la aplicación o la instancia HTTP request en los constructores de otros objetos. Por ejemplo, la siguiente vinculación inyecta todo el contenedor de servicios en un objeto que está registrado como 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 Service se resuelve durante el proceso de arranque de la aplicación, el contenedor será inyectado en el servicio y ese mismo contenedor será retenido por la instancia Service en solicitudes posteriores. Esto puede no ser un problema para su aplicación en particular; sin embargo, puede causar que el contenedor carezca inesperadamente de bindings que se añadieron más tarde en el ciclo de arranque o por una solicitud posterior.
Como solución, podría dejar de registrar la vinculación como singleton, o podría inyectar un closure resolutor 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 helper 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 la request
En general, debe evitar inyectar el contenedor de servicios de la aplicación o la instancia HTTP request en los constructores de otros objetos. Por ejemplo, la siguiente vinculación inyecta toda la instancia de la request en un objeto que está registrado como 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 Service se resuelve durante el proceso de arranque de la aplicación, la request HTTP será inyectada en el servicio y esa misma request será retenida por la instancia Service en solicitudes posteriores. Por lo tanto, todos los encabezados, entradas y datos de la cadena de consulta serán incorrectos, así como cualquier otro dato de la request.
Como solución, podría dejar de registrar la vinculación como singleton, o podría inyectar un closure resolutor de la request en el servicio que siempre resuelva la instancia actual de la request. O, la forma más recomendada es simplemente pasar la información específica de la request que su 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']);
});
// O...
$service->method($request->input('name'));
El helper global request siempre devolverá la request que la aplicación está manejando actualmente y por lo tanto es seguro usarlo dentro de su aplicación.
Es aceptable usar type-hint de la instancia Illuminate\Http\Request en los métodos de sus controladores y closures de rutas.
#Inyección del repositorio de configuración
En general, debe 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 está registrado como 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, podría dejar de registrar la vinculación como singleton, o podría inyectar un closure resolutor del repositorio de configuración a 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 helper global config siempre devolverá la versión más reciente del repositorio de configuración y por lo tanto es seguro usarlo dentro de su aplicación.
#Manejo de fugas de memoria
Recuerde, Octane mantiene su aplicación en memoria entre solicitudes; por lo tanto, agregar datos a un array estáticamente mantenido 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 al array estático $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 su aplicación, debe tener especial cuidado para evitar crear este tipo de fugas de memoria. Se recomienda monitorear el uso de memoria de su aplicación durante el desarrollo local para asegurarse de no introducir nuevas fugas de memoria en su aplicación.
#Tareas concurrentes
Esta característica requiere Swoole.
Al usar Swoole, puede ejecutar operaciones concurrentemente mediante tareas ligeras en segundo plano. Puede lograr esto usando el método concurrently de Octane. Puede combinar este método con la desestructuración de arrays en PHP para obtener 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 "task workers" de Swoole y se ejecutan en un proceso completamente diferente al de la solicitud entrante. La cantidad de workers disponibles para procesar tareas concurrentes se determina con 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 debe proporcionar más de 1024 tareas debido a limitaciones impuestas por el sistema de tareas de Swoole.
#Ticks e intervalos
Esta característica requiere Swoole.
Al usar Swoole, puede registrar operaciones "tick" que se ejecutarán cada cierto número de segundos especificado. Puede registrar callbacks "tick" mediante el método tick. El primer argumento que se pasa al método tick debe ser un string que representa el nombre del ticker. El segundo argumento debe ser un callable que se invocará en el intervalo especificado.
En este ejemplo, registraremos un closure para que se invoque cada 10 segundos. Normalmente, el método tick debe llamarse dentro del método boot de uno de los service providers de su aplicación:
Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10);
Usando el método immediate, puede indicar a Octane que invoque inmediatamente el callback 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
Esta característica requiere Swoole.
Al usar Swoole, puede aprovechar el driver de caché Octane, que ofrece velocidades de lectura y escritura de hasta 2 millones de operaciones por segundo. Por lo tanto, este driver de caché es una excelente opción para aplicaciones que necesitan velocidades extremas de lectura/escritura en su capa de caché.
Este driver de caché está potenciado por Swoole tables. Todos los datos almacenados en la caché están disponibles para todos los workers en el servidor. Sin embargo, los datos en caché se borrarán cuando el servidor se reinicie:
Cache::store('octane')->put('framework', 'Laravel', 30);
El número máximo de entradas permitidas en la caché Octane puede definirse en el archivo de configuración octane de su aplicación.
#Intervalos de caché
Además de los métodos típicos proporcionados por el sistema de caché de Laravel, el driver de caché Octane cuenta con cachés basados en intervalos. Estas cachés se refrescan automáticamente en el intervalo especificado y deben registrarse dentro del método boot de uno de los service providers de su aplicación. Por ejemplo, la siguiente caché se refrescará cada cinco segundos:
use Illuminate\Support\Str;
Cache::store('octane')->interval('random', function () {
return Str::random(10);
}, seconds: 5);
#Tablas
Esta característica requiere Swoole.
Al usar Swoole, puede definir e interactuar con sus propias Swoole tables arbitrarias. Las tablas Swoole ofrecen un rendimiento extremo y los datos en estas tablas pueden ser accedidos por todos los workers en el servidor. Sin embargo, los datos dentro de ellas se perderán cuando el servidor se reinicie.
Las tablas deben definirse dentro del array de configuración tables en el archivo de configuración octane de su aplicación. Un ejemplo de tabla que permite un máximo de 1000 filas ya está configurado para usted. El tamaño máximo de las columnas string puede configurarse especificando el tamaño de la columna después del tipo de columna como se muestra a continuación:
'tables' => [
'example:1000' => [
'name' => 'string:1000',
'votes' => 'int',
],
],
Para acceder a una tabla, puede usar 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');
Los tipos de columna soportados por las tablas Swoole son: string, int y float.