|
|
# Usando los Workers de FrankenPHP
|
||
|
|
|
||
|
|
Inicia tu aplicaci贸n una vez y mant茅nla en memoria.
|
||
|
|
FrankenPHP gestionar谩 las peticiones entrantes en unos pocos milisegundos.
|
||
|
|
|
||
|
|
## Iniciando Scripts de Worker
|
||
|
|
|
||
|
|
### Docker
|
||
|
|
|
||
|
|
Establece el valor de la variable de entorno `FRANKENPHP_CONFIG` a `worker /ruta/a/tu/script/worker.php`:
|
||
|
|
|
||
|
|
```console
|
||
|
|
docker run \
|
||
|
|
-e FRANKENPHP_CONFIG="worker /app/ruta/a/tu/script/worker.php" \
|
||
|
|
-v $PWD:/app \
|
||
|
|
-p 80:80 -p 443:443 -p 443:443/udp \
|
||
|
|
dunglas/frankenphp
|
||
|
|
```
|
||
|
|
|
||
|
|
### Binario Aut贸nomo
|
||
|
|
|
||
|
|
Usa la opci贸n `--worker` del comando `php-server` para servir el contenido del directorio actual usando un worker:
|
||
|
|
|
||
|
|
```console
|
||
|
|
frankenphp php-server --worker /ruta/a/tu/script/worker.php
|
||
|
|
```
|
||
|
|
|
||
|
|
Si tu aplicaci贸n PHP est谩 [incrustada en el binario](embed.md), puedes agregar un `Caddyfile` personalizado en el directorio ra铆z de la aplicaci贸n.
|
||
|
|
Ser谩 usado autom谩ticamente.
|
||
|
|
|
||
|
|
Tambi茅n es posible [reiniciar el worker al detectar cambios en archivos](config.md#watching-for-file-changes) con la opci贸n `--watch`.
|
||
|
|
El siguiente comando activar谩 un reinicio si alg煤n archivo que termine en `.php` en el directorio `/ruta/a/tu/app/` o sus subdirectorios es modificado:
|
||
|
|
|
||
|
|
```console
|
||
|
|
frankenphp php-server --worker /ruta/a/tu/script/worker.php --watch="/ruta/a/tu/app/**/*.php"
|
||
|
|
```
|
||
|
|
|
||
|
|
Esta funci贸n se utiliza frecuentemente en combinaci贸n con [hot reloading](hot-reload.md).
|
||
|
|
|
||
|
|
## Symfony Runtime
|
||
|
|
|
||
|
|
> [!TIP]
|
||
|
|
> La siguiente secci贸n es necesaria solo para versiones anteriores a Symfony 7.4, donde se introdujo soporte nativo para el modo worker de FrankenPHP.
|
||
|
|
|
||
|
|
El modo worker de FrankenPHP es soportado por el [Componente Runtime de Symfony](https://symfony.com/doc/current/components/runtime.html).
|
||
|
|
Para iniciar cualquier aplicaci贸n Symfony en un worker, instala el paquete FrankenPHP de [PHP Runtime](https://github.com/php-runtime/runtime):
|
||
|
|
|
||
|
|
```console
|
||
|
|
composer require runtime/frankenphp-symfony
|
||
|
|
```
|
||
|
|
|
||
|
|
Inicia tu servidor de aplicaci贸n definiendo la variable de entorno `APP_RUNTIME` para usar el Runtime de FrankenPHP Symfony:
|
||
|
|
|
||
|
|
```console
|
||
|
|
docker run \
|
||
|
|
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
|
||
|
|
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
|
||
|
|
-v $PWD:/app \
|
||
|
|
-p 80:80 -p 443:443 -p 443:443/udp \
|
||
|
|
dunglas/frankenphp
|
||
|
|
```
|
||
|
|
|
||
|
|
## Laravel Octane
|
||
|
|
|
||
|
|
Consulta [la documentaci贸n dedicada](laravel.md#laravel-octane).
|
||
|
|
|
||
|
|
## Aplicaciones Personalizadas
|
||
|
|
|
||
|
|
El siguiente ejemplo muestra c贸mo crear tu propio script de worker sin depender de una biblioteca de terceros:
|
||
|
|
|
||
|
|
```php
|
||
|
|
<?php
|
||
|
|
// public/index.php
|
||
|
|
|
||
|
|
// Prevenir la terminaci贸n del script de worker cuando una conexi贸n de cliente se interrumpe
|
||
|
|
ignore_user_abort(true);
|
||
|
|
|
||
|
|
// Iniciar tu aplicaci贸n
|
||
|
|
require __DIR__.'/vendor/autoload.php';
|
||
|
|
|
||
|
|
$myApp = new \App\Kernel();
|
||
|
|
$myApp->boot();
|
||
|
|
|
||
|
|
// Manejador fuera del bucle para mejor rendimiento (menos trabajo)
|
||
|
|
$handler = static function () use ($myApp) {
|
||
|
|
try {
|
||
|
|
// Llamado cuando se recibe una petici贸n,
|
||
|
|
// las superglobales, php://input y similares se reinician
|
||
|
|
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
|
||
|
|
} catch (\Throwable $exception) {
|
||
|
|
// `set_exception_handler` se llama solo cuando el script de worker termina,
|
||
|
|
// lo cual puede no ser lo que esperas, as铆 que captura y maneja excepciones aqu铆
|
||
|
|
(new \MyCustomExceptionHandler)->handleException($exception);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
|
||
|
|
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
|
||
|
|
$keepRunning = \frankenphp_handle_request($handler);
|
||
|
|
|
||
|
|
// Haz algo despu茅s de enviar la respuesta HTTP
|
||
|
|
$myApp->terminate();
|
||
|
|
|
||
|
|
// Llama al recolector de basura para reducir las posibilidades de que se active en medio de la generaci贸n de una p谩gina
|
||
|
|
gc_collect_cycles();
|
||
|
|
|
||
|
|
if (!$keepRunning) break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Limpieza
|
||
|
|
$myApp->shutdown();
|
||
|
|
```
|
||
|
|
|
||
|
|
Luego, inicia tu aplicaci贸n y usa la variable de entorno `FRANKENPHP_CONFIG` para configurar tu worker:
|
||
|
|
|
||
|
|
```console
|
||
|
|
docker run \
|
||
|
|
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
|
||
|
|
-v $PWD:/app \
|
||
|
|
-p 80:80 -p 443:443 -p 443:443/udp \
|
||
|
|
dunglas/frankenphp
|
||
|
|
```
|
||
|
|
|
||
|
|
Por omisi贸n, se inician 2 workers por CPU.
|
||
|
|
Tambi茅n puedes configurar el n煤mero de workers a iniciar:
|
||
|
|
|
||
|
|
```console
|
||
|
|
docker run \
|
||
|
|
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
|
||
|
|
-v $PWD:/app \
|
||
|
|
-p 80:80 -p 443:443 -p 443:443/udp \
|
||
|
|
dunglas/frankenphp
|
||
|
|
```
|
||
|
|
|
||
|
|
### Reiniciar el Worker Despu茅s de un N煤mero Determinado de Peticiones
|
||
|
|
|
||
|
|
Como PHP no fue dise帽ado originalmente para procesos de larga duraci贸n, a煤n hay muchas bibliotecas y c贸digos heredados que generan fugas de memoria.
|
||
|
|
Una soluci贸n para usar este tipo de c贸digo en modo worker es reiniciar el script de worker despu茅s de procesar un cierto n煤mero de peticiones:
|
||
|
|
|
||
|
|
El fragmento de worker anterior permite configurar un n煤mero m谩ximo de peticiones a manejar estableciendo una variable de entorno llamada `MAX_REQUESTS`.
|
||
|
|
|
||
|
|
### Reiniciar Workers Manualmente
|
||
|
|
|
||
|
|
Aunque es posible reiniciar workers [al detectar cambios en archivos](config.md#watching-for-file-changes), tambi茅n es posible reiniciar todos los workers
|
||
|
|
de manera controlada a trav茅s de la [API de administraci贸n de Caddy](https://caddyserver.com/docs/api). Si el admin est谩 habilitado en tu
|
||
|
|
[Caddyfile](config.md#caddyfile-config), puedes activar el endpoint de reinicio con una simple petici贸n POST como esta:
|
||
|
|
|
||
|
|
```console
|
||
|
|
curl -X POST http://localhost:2019/frankenphp/workers/restart
|
||
|
|
```
|
||
|
|
|
||
|
|
### Fallos en Workers
|
||
|
|
|
||
|
|
Si un script de worker falla con un c贸digo de salida distinto de cero, FrankenPHP lo reiniciar谩 con una estrategia de retroceso exponencial.
|
||
|
|
Si el script de worker permanece activo m谩s tiempo que el 煤ltimo retroceso * 2,
|
||
|
|
no penalizar谩 al script de worker y lo reiniciar谩 nuevamente.
|
||
|
|
Sin embargo, si el script de worker contin煤a fallando con un c贸digo de salida distinto de cero en un corto per铆odo de tiempo
|
||
|
|
(por ejemplo, tener un error tipogr谩fico en un script), FrankenPHP fallar谩 con el error: `too many consecutive failures`.
|
||
|
|
|
||
|
|
El n煤mero de fallos consecutivos puede configurarse en tu [Caddyfile](config.md#caddyfile-config) con la opci贸n `max_consecutive_failures`:
|
||
|
|
|
||
|
|
```caddyfile
|
||
|
|
frankenphp {
|
||
|
|
worker {
|
||
|
|
# ...
|
||
|
|
max_consecutive_failures 10
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Comportamiento de las Superglobales
|
||
|
|
|
||
|
|
Las [superglobales de PHP](https://www.php.net/manual/es/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...)
|
||
|
|
se comportan de la siguiente manera:
|
||
|
|
|
||
|
|
- antes de la primera llamada a `frankenphp_handle_request()`, las superglobales contienen valores vinculados al script de worker en s铆
|
||
|
|
- durante y despu茅s de la llamada a `frankenphp_handle_request()`, las superglobales contienen valores generados a partir de la petici贸n HTTP procesada; cada llamada a `frankenphp_handle_request()` cambia los valores de las superglobales
|
||
|
|
|
||
|
|
Para acceder a las superglobales del script de worker dentro de la retrollamada, debes copiarlas e importar la copia en el 谩mbito de la retrollamada:
|
||
|
|
|
||
|
|
```php
|
||
|
|
<?php
|
||
|
|
// Copia la superglobal $_SERVER del worker antes de la primera llamada a frankenphp_handle_request()
|
||
|
|
$workerServer = $_SERVER;
|
||
|
|
|
||
|
|
$handler = static function () use ($workerServer) {
|
||
|
|
var_dump($_SERVER); // $_SERVER vinculado a la petici贸n
|
||
|
|
var_dump($workerServer); // $_SERVER del script de worker
|
||
|
|
};
|
||
|
|
|
||
|
|
// ...
|
||
|
|
```
|