From 855eae6cecea1c4b91c49ea6cb4c2ff2d8ea9ae3 Mon Sep 17 00:00:00 2001 From: dunglas <57224+dunglas@users.noreply.github.com> Date: Wed, 21 Oct 2026 10:58:05 +0000 Subject: [PATCH] docs: update translations --- docs/cn/worker.md | 149 ++++++++++++++++++++++++-------- docs/fr/worker.md | 160 ++++++++++++++++++++++++++--------- docs/it/worker.md | 97 +++++++++++++++++---- docs/ja/worker.md | 143 +++++++++++++++++++++++-------- docs/pt-br/worker.md | 196 ++++++++++++++++++++++++++++++------------- docs/ru/worker.md | 138 ++++++++++++++++++++++-------- docs/tr/worker.md | 157 ++++++++++++++++++++++++++-------- 7 files changed, 784 insertions(+), 256 deletions(-) diff --git a/docs/cn/worker.md b/docs/cn/worker.md index e1a2129f7f..1de9ab52ce 100644 --- a/docs/cn/worker.md +++ b/docs/cn/worker.md @@ -1,15 +1,20 @@ +--- +title: FrankenPHP worker 模式:保持你的 PHP 应用程序在内存中 +description: 以 worker 模式运行 FrankenPHP,使你的 PHP 应用程序在请求之间保持启动状态,减少启动开销,并在几毫秒内提供响应。 +--- + # 使用 FrankenPHP workers -启动一次应用程序并将其保存在内存中。 +启动你的应用程序一次并将其保存在内存中。 FrankenPHP 将在几毫秒内处理传入请求。 -## 启动 worker 脚本 +## 启动 FrankenPHP worker 脚本 -### Docker +### 使用 Docker 运行 FrankenPHP worker 将 `FRANKENPHP_CONFIG` 环境变量的值设置为 `worker /path/to/your/worker/script.php`: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \ -v $PWD:/app \ @@ -17,11 +22,11 @@ docker run \ dunglas/frankenphp ``` -### 独立二进制文件 +### 使用独立二进制文件运行 FrankenPHP worker -使用 `php-server` 命令的 `--worker` 选项通过 worker 为当前目录的内容提供服务: +使用 `php-server` 命令的 `--worker` 选项,通过 worker 为当前目录的内容提供服务: -```console +```bash frankenphp php-server --worker /path/to/your/worker/script.php ``` @@ -31,40 +36,21 @@ frankenphp php-server --worker /path/to/your/worker/script.php 还可以使用 `--watch` 选项在[文件更改时重启 worker](config.md#watching-for-file-changes)。 如果 `/path/to/your/app/` 目录或子目录中任何以 `.php` 结尾的文件被修改,以下命令将触发重启: -```console +```bash frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php" ``` 此功能通常与[热重载](hot-reload.md)结合使用。 -## Symfony Runtime - -> [!TIP] -> 以下部分仅在 Symfony 7.4 之前是必需的,因为 Symfony 7.4 引入了对 FrankenPHP worker 模式的原生支持。 - -FrankenPHP 的 worker 模式由 [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) 支持。 -要在 worker 中启动任何 Symfony 应用程序,请安装 [PHP Runtime](https://github.com/php-runtime/runtime) 的 FrankenPHP 包: - -```console -composer require runtime/frankenphp-symfony -``` - -通过定义 `APP_RUNTIME` 环境变量来使用 FrankenPHP Symfony Runtime 启动你的应用服务器: +## 适用于 Symfony 的 Worker 模式 -```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 -``` +请参阅[FrankenPHP Symfony worker 模式文档](symfony.md#symfony-worker-mode-with-frankenphp)。 -## Laravel Octane +## 适用于 Laravel Octane 的 Worker 模式 -请参阅[专门的文档](laravel.md#laravel-octane)。 +请参阅[FrankenPHP Laravel Octane 文档](laravel.md#laravel-octane)。 -## 自定义应用程序 +## 编写自定义 FrankenPHP worker 脚本 以下示例展示了如何创建自己的 worker 脚本而不依赖第三方库: @@ -110,7 +96,7 @@ $myApp->shutdown(); 然后,启动你的应用程序并使用 `FRANKENPHP_CONFIG` 环境变量配置你的 worker: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -v $PWD:/app \ @@ -121,7 +107,7 @@ docker run \ 默认情况下,每个 CPU 启动 2 个 worker。 你也可以配置要启动的 worker 数量: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php 42" \ -v $PWD:/app \ @@ -129,9 +115,57 @@ docker run \ dunglas/frankenphp ``` +### 使用 PSR-15 + +如果你的应用程序使用 [PSR-15](https://www.php-fig.org/psr/psr-15/) 而不是超全局变量,请使用 [`nyholm/psr7`](https://github.com/Nyholm/psr7) 和 [`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server) 在处理器的边缘转换请求/响应: + +```console +composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler +``` + +```php +boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### 在处理一定数量的请求后重启 worker -由于 PHP 最初不是为长时间运行的进程而设计的,仍有许多库和传统代码会泄漏内存。 +由于 PHP 最初不是为长时间运行的进程而设计的,许多库和传统代码仍可能导致内存泄漏。 在 worker 模式下使用此类代码的一个解决方法是在处理一定数量的请求后重启 worker 脚本: 前面的 worker 代码片段允许通过设置名为 `MAX_REQUESTS` 的环境变量来配置要处理的最大请求数。 @@ -140,7 +174,7 @@ docker run \ 虽然可以在[文件更改时重启 workers](config.md#watching-for-file-changes),但也可以通过 [Caddy admin API](https://caddyserver.com/docs/api) 优雅地重启所有 workers。如果在你的 [Caddyfile](config.md#caddyfile-config) 中启用了 admin,你可以通过简单的 POST 请求 ping 重启端点,如下所示: -```console +```bash curl -X POST http://localhost:2019/frankenphp/workers/restart ``` @@ -163,6 +197,13 @@ frankenphp { } ``` +### 线程数 + +当 worker 模式启用时,需要一个额外的线程来处理传入请求, +因此默认的 `num_threads` 值为 `1 + 2x(可用 CPU 数量)`。 + +因此,在该模式下,如果 `num_threads` 和 `num` 被显式设置,则 `num_threads` 必须严格大于 `num`。 + ## 超全局变量行为 [PHP 超全局变量](https://www.php.net/manual/language.variables.superglobals.php)(`$_SERVER`、`$_ENV`、`$_GET`...) @@ -185,3 +226,39 @@ $handler = static function () use ($workerServer) { // ... ``` + +大多数超全局变量(`$_GET`、`$_POST`、`$_COOKIE`、`$_FILES`、`$_SERVER`、`$_REQUEST`)会在请求之间自动重置。 +然而,**`$_ENV` 当前不会在请求之间重置**。 +这意味着在请求期间对 `$_ENV` 所做的任何修改都将持续存在,并对同一 worker 线程处理的后续请求可见。 +避免在 `$_ENV` 中存储与请求相关或敏感的数据。 + +## 状态持久性 + +由于 worker 模式使 PHP 进程在请求之间保持活动状态,以下状态会在请求之间持久化: + +- **静态变量**:函数或方法中用 `static` 声明的变量在请求之间保持其值。 +- **类静态属性**:类的静态属性在请求之间持久化。 +- **全局变量**:worker 脚本全局范围内的变量在请求之间持久化。 +- **内存缓存**:在请求处理器之外存储在内存中的任何数据(数组、对象)都将持久化。 +- **运行时引擎设置**:使用 `ini_set()`、`stream_context_set_default()`、`date_default_timezone_set()`、`chdir()`、`stream_wrapper_register()`、`set_error_handler()` 或 `set_exception_handler()` 进行的更改对后续请求仍然有效。切勿在此处存储与请求或用户相关的特定值,例如默认流上下文中的凭据:请为每个调用传递一个显式上下文。 + +这是设计使然,也是 worker 模式快速的原因。然而,它需要注意避免意外的副作用: + +```php + [!TIP] -> La section suivante est nécessaire uniquement avant Symfony 7.4, où le support natif du mode worker de FrankenPHP a été introduit. +## Mode worker pour Symfony -Le mode worker de FrankenPHP est pris en charge par le [composant Runtime de Symfony](https://symfony.com/doc/current/components/runtime.html). -Pour démarrer une application Symfony dans un worker, installez le package FrankenPHP de [PHP Runtime](https://github.com/php-runtime/runtime) : - -```console -composer require runtime/frankenphp-symfony -``` - -Démarrez votre serveur d'application en définissant la variable d'environnement `APP_RUNTIME` pour utiliser le Runtime Symfony de FrankenPHP : - -```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 -``` +Voir [la documentation du mode worker de Symfony avec FrankenPHP](symfony.md#symfony-worker-mode-with-frankenphp). -## Laravel Octane +## Mode worker pour Laravel Octane -Voir [la documentation dédiée](laravel.md#laravel-octane). +Voir [la documentation FrankenPHP Laravel Octane](laravel.md#laravel-octane). -## Applications personnalisées +## Écrire un script worker FrankenPHP personnalisé L'exemple suivant montre comment créer votre propre script worker sans dépendre d'une bibliothèque tierce : @@ -78,7 +64,7 @@ require __DIR__.'/vendor/autoload.php'; $myApp = new \App\Kernel(); $myApp->boot(); -// Déclarer le handler en dehors de la boucle pour de meilleures performances (moins de travail effectué) +// Handler en dehors de la boucle pour de meilleures performances (moins de travail) $handler = static function () use ($myApp) { try { // Appelé lorsqu'une requête est reçue, @@ -98,7 +84,7 @@ for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests // Faire quelque chose après l'envoi de la réponse HTTP $myApp->terminate(); - // Exécuter le ramasse-miettes pour réduire les chances qu'il soit déclenché au milieu de la génération d'une page + // Appeler le ramasse-miettes pour réduire les chances qu'il soit déclenché au milieu de la génération d'une page gc_collect_cycles(); if (!$keepRunning) break; @@ -110,7 +96,7 @@ $myApp->shutdown(); Ensuite, démarrez votre application et utilisez la variable d'environnement `FRANKENPHP_CONFIG` pour configurer votre worker : -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -v $PWD:/app \ @@ -121,7 +107,7 @@ docker run \ Par défaut, 2 workers par CPU sont démarrés. Vous pouvez également configurer le nombre de workers à démarrer : -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php 42" \ -v $PWD:/app \ @@ -129,6 +115,54 @@ docker run \ dunglas/frankenphp ``` +### Utiliser PSR-15 + +Si votre application utilise [PSR-15](https://www.php-fig.org/psr/psr-15/) au lieu des superglobales, convertissez la requête/réponse aux limites du handler avec [`nyholm/psr7`](https://github.com/Nyholm/psr7) et [`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server) : + +```bash +composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler +``` + +```php +boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### Redémarrer le worker après un certain nombre de requêtes Comme PHP n'a pas été initialement conçu pour des processus de longue durée, de nombreuses bibliothèques et codes anciens présentent encore des fuites de mémoire. @@ -143,16 +177,17 @@ il est également possible de redémarrer tous les workers de manière élégant Si l'administration est activée dans votre [Caddyfile](config.md#caddyfile-config), vous pouvez envoyer un ping à l'endpoint de redémarrage avec une simple requête POST comme celle-ci : -```console +```bash curl -X POST http://localhost:2019/frankenphp/workers/restart ``` ### Échecs des workers -Si un script de worker se plante avec un code de sortie non nul, FrankenPHP le redémarre avec une stratégie de backoff exponentielle. -Si le script worker reste en place plus longtemps que le dernier backoff × 2, FrankenPHP ne pénalisera pas le script et le redémarrera à nouveau. -Toutefois, si le script de worker continue d'échouer avec un code de sortie non nul dans un court laps de temps -(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures` (trop d'échecs consécutifs). +Si un script worker crash avec un code de sortie non nul, FrankenPHP le redémarrera avec une stratégie de backoff exponentielle. +Si le script worker reste en place plus longtemps que le dernier backoff × 2, +il ne pénalisera pas le script worker et le redémarrera à nouveau. +Toutefois, si le script worker continue d'échouer avec un code de sortie non nul dans un court laps de temps +(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures`. Le nombre d'échecs consécutifs peut être configuré dans votre [Caddyfile](config.md#caddyfile-config) avec l'option `max_consecutive_failures` : @@ -165,6 +200,13 @@ frankenphp { } ``` +### Nombre de threads + +Lorsque le mode worker est activé, un thread supplémentaire est nécessaire pour gérer les requêtes entrantes, +donc la valeur par défaut de `num_threads` est `1 + 2x(nombre de CPUs disponibles)`. + +Ainsi, dans ce mode, si `num_threads` et `num` sont explicitement définis, `num_threads` doit être strictement supérieur à `num`. + ## Comportement des superglobales [Les superglobales PHP](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) @@ -173,7 +215,7 @@ se comportent comme suit : - avant le premier appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs liées au script worker lui-même - pendant et après l'appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs générées à partir de la requête HTTP traitée, chaque appel à `frankenphp_handle_request()` change les valeurs des superglobales -Pour accéder aux superglobales du script worker à l'intérieur de la fonction de rappel, vous devez les copier et importer la copie dans le scope de la fonction : +Pour accéder aux superglobales du script worker à l'intérieur de la fonction de rappel, vous devez les copier et importer la copie dans la portée de la fonction de rappel : ```php boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### Riavvia il worker dopo un certo numero di richieste -Poiché PHP non è stato originariamente progettato per processi di lunga durata, ci sono ancora molte librerie e codici legacy che perdono memoria. +Poiché PHP non è stato originariamente progettato per processi di lunga durata, molte librerie e codici legacy perdono ancora memoria. Una soluzione alternativa all'utilizzo di questo tipo di codice in modalità worker consiste nel riavviare lo script worker dopo aver elaborato un certo numero di richieste: -Il precedente frammento di lavoro consente di configurare un numero massimo di richieste da gestire impostando una variabile di ambiente denominata `MAX_REQUESTS`. +Il precedente script worker consente di configurare un numero massimo di richieste da gestire impostando una variabile di ambiente denominata `MAX_REQUESTS`. ### Riavvia i worker manualmente -Sebbene sia possibile riavviare i worker [in seguito alle modifiche dei file](config.md#controllo-delle-modifiche-ai-file), è anche possibile riavviare tutti i worker -tramite l'[API di amministrazione Caddy](https://caddyserver.com/docs/api). Se l'amministratore è abilitato in +Sebbene sia possibile riavviare i worker [in seguito alle modifiche dei file](config.md#watching-for-file-changes), è anche possibile riavviare tutti i worker +in modo elegante tramite l'[API di amministrazione Caddy](https://caddyserver.com/docs/api). Se l'amministratore è abilitato nel tuo [Caddyfile](config.md#caddyfile-config), si può eseguire il ping dell'endpoint di riavvio con una semplice richiesta POST: ```bash @@ -129,13 +182,13 @@ curl -X POST http://localhost:2019/frankenphp/workers/restart ### Fallimenti dei worker -Se un worker si blocca con un codice di uscita diverso da zero, FrankenPHP lo riavvierà con una strategia di backoff esponenziale. +Se uno script worker si blocca con un codice di uscita diverso da zero, FrankenPHP lo riavvierà con una strategia di backoff esponenziale. Se lo script worker rimane attivo più a lungo dell'ultimo backoff moltiplicato per due, non penalizzerà lo script worker e lo riavvierà di nuovo. -Tuttavia, se il worker continua a fallire con un codice di uscita diverso da zero in un breve periodo di tempo +Tuttavia, se lo script worker continua a fallire con un codice di uscita diverso da zero in un breve periodo di tempo (ad esempio, avendo un errore di battitura in uno script), FrankenPHP si bloccherà con l'errore: `too many consecutive failures`. -Il numero di errori consecutivi può essere configurato nel [Caddyfile](config.md#caddyfile-config) con l'opzione `max_consecutive_failures`: +Il numero di errori consecutivi può essere configurato nel tuo [Caddyfile](config.md#caddyfile-config) con l'opzione `max_consecutive_failures`: ```caddyfile frankenphp { @@ -146,6 +199,13 @@ frankenphp { } ``` +### Numero di thread + +Quando la modalità worker è abilitata, è richiesto un thread aggiuntivo per gestire le richieste in arrivo, +quindi il valore predefinito di `num_threads` è `1 + 2x(numero di CPU disponibili)`. + +Pertanto, in tale modalità se `num_threads` e `num` sono impostati esplicitamente, `num_threads` deve essere strettamente maggiore di `num`. + ## Comportamento dei superglobali [I superglobali PHP](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) @@ -180,8 +240,9 @@ Poiché la modalità worker mantiene attivo il processo PHP tra le richieste, il - **Variabili statiche**: le variabili dichiarate con `static` all'interno di funzioni o metodi mantengono i loro valori tra le richieste. - **Proprietà statiche della classe**: le proprietà statiche sulle classi persistono tra le richieste. -- **Variabili globali**: le variabili nell'ambito globale del worker persistono tra le richieste. +- **Variabili globali**: le variabili nell'ambito globale dello script worker persistono tra le richieste. - **Cache in memoria**: tutti i dati archiviati in memoria (array, oggetti) all'esterno del gestore delle richieste persistono. +- **Impostazioni del motore di runtime**: le modifiche apportate con `ini_set()`, `stream_context_set_default()`, `date_default_timezone_set()`, `chdir()`, `stream_wrapper_register()`, `set_error_handler()` o `set_exception_handler()` rimangono in vigore per le richieste successive. Non archiviare mai valori specifici della richiesta o dell'utente in queste impostazioni, come le credenziali nel contesto del flusso predefinito: passa invece un contesto esplicito ad ogni chiamata. Questo è previsto dalla progettazione ed è ciò che rende veloce la modalità worker. Tuttavia, richiede attenzione per evitare effetti collaterali indesiderati: @@ -201,5 +262,5 @@ while (\frankenphp_handle_request($handler)) { } ``` -Quando si scrivono worker, assicurarsi di reimpostare qualsiasi stato specifico della richiesta tra le richieste. +Quando si scrivono script worker, assicurarsi di reimpostare qualsiasi stato specifico della richiesta tra le richieste. Framework come [Symfony](symfony.md) e [Laravel Octane](laravel.md) si occupano di ripristinare automaticamente la maggior parte degli stati, ma potrebbe comunque essere necessario ripristinare alcuni servizi. Con Symfony, i servizi che mantengono uno stato specifico della richiesta dovrebbero implementare [`Symfony\Contracts\Service\ResetInterface`](https://github.com/symfony/contracts/blob/main/Service/ResetInterface.php) in modo che vengano reimpostati dal kernel tra una richiesta e l'altra. diff --git a/docs/ja/worker.md b/docs/ja/worker.md index ed8880f8bf..aae5cf4c04 100644 --- a/docs/ja/worker.md +++ b/docs/ja/worker.md @@ -1,15 +1,20 @@ +--- +title: FrankenPHP worker mode: PHPアプリをメモリに保持する +description: FrankenPHPをワーカーモードで実行すると、PHPアプリケーションをリクエスト間で起動状態に保ち、起動オーバーヘッドを削減し、ミリ秒単位でレスポンスを提供できます。 +--- + # FrankenPHPワーカーの使用 アプリケーションを一度起動してメモリに保持します。 FrankenPHPは数ミリ秒で受信リクエストを処理します。 -## ワーカースクリプトの開始 +## FrankenPHPワーカー スクリプトの開始 -### Docker +### DockerでFrankenPHPワーカーを実行する `FRANKENPHP_CONFIG`環境変数の値を`worker /path/to/your/worker/script.php`に設定します: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \ -v $PWD:/app \ @@ -17,11 +22,11 @@ docker run \ dunglas/frankenphp ``` -### スタンドアロンバイナリ +### スタンドアロンバイナリでFrankenPHPワーカーを実行する `php-server`コマンドの`--worker`オプションを使って、現在のディレクトリのコンテンツをワーカーを通じて提供できます: -```console +```bash frankenphp php-server --worker /path/to/your/worker/script.php ``` @@ -31,40 +36,21 @@ PHPアプリが[バイナリに埋め込まれている](embed.md)場合は、 また、`--watch`オプションを使えば、[ファイルの変更に応じてワーカーを再起動](config.md#watching-for-file-changes)することも可能です。 以下のコマンドは、`/path/to/your/app/`ディレクトリおよびそのサブディレクトリ内の`.php`で終わるファイルが変更された場合に再起動をトリガーします: -```console +```bash frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php" ``` この機能は、[ホットリロード](hot-reload.md)と組み合わせてよく使用されます。 -## Symfonyランタイム - -> [!TIP] -> 以下のセクションは、FrankenPHPワーカーモードのネイティブサポートが導入されたSymfony 7.4より前のバージョンでのみ必要です。 - -FrankenPHPのワーカーモードは[Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html)によってサポートされています。 -ワーカーでSymfonyアプリケーションを開始するには、FrankenPHP用の[PHP Runtime](https://github.com/php-runtime/runtime)パッケージをインストールします: - -```console -composer require runtime/frankenphp-symfony -``` - -アプリケーションサーバーを起動するには、FrankenPHP Symfony Runtimeを使用するように`APP_RUNTIME`環境変数を定義します: +## 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 -``` +[FrankenPHP Symfonyワーカーモードのドキュメント](symfony.md#symfony-worker-mode-with-frankenphp)を参照してください。 -## Laravel Octane +## Laravel Octaneのワーカーモード -[専用ドキュメント](laravel.md#laravel-octane)を参照してください。 +[FrankenPHP Laravel Octaneのドキュメント](laravel.md#laravel-octane)を参照してください。 -## カスタムアプリ +## カスタムFrankenPHPワーカー スクリプトの作成 以下の例は、サードパーティライブラリに依存せずに独自のワーカースクリプトを作成する方法を示しています: @@ -110,7 +96,7 @@ $myApp->shutdown(); 次に、アプリを開始し、`FRANKENPHP_CONFIG`環境変数を使用してワーカーを設定します: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -v $PWD:/app \ @@ -121,7 +107,7 @@ docker run \ デフォルトでは、CPU当たり2つのワーカーが開始されます。 開始するワーカー数を設定することもできます: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php 42" \ -v $PWD:/app \ @@ -129,6 +115,54 @@ docker run \ dunglas/frankenphp ``` +### PSR-15の使用 + +アプリケーションがスーパーグローバルではなく[PSR-15](https://www.php-fig.org/psr/psr-15/)を使用する場合、ハンドラーの境界で[`nyholm/psr7`](https://github.com/Nyholm/psr7)と[`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server)を使用してリクエスト/レスポンスを変換します: + +```console +composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler +``` + +```php +boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### 一定数のリクエスト処理後にワーカーを再起動する PHPはもともと長時間実行されるプロセス向けに設計されていなかったため、メモリリークを引き起こすライブラリやレガシーコードがいまだに多く存在します。 @@ -142,7 +176,7 @@ PHPはもともと長時間実行されるプロセス向けに設計されて [Caddy admin API](https://caddyserver.com/docs/api)を使用してすべてのワーカーをグレースフルに(安全に)再起動することも可能です。adminが [Caddyfile](config.md#caddyfile-config)で有効になっている場合、次のような単純なPOSTリクエストで再起動エンドポイントにpingできます: -```console +```bash curl -X POST http://localhost:2019/frankenphp/workers/restart ``` @@ -165,6 +199,13 @@ frankenphp { } ``` +### スレッド数 + +ワーカーモードが有効な場合、受信リクエストを処理するために追加のスレッドが必要になるため、 +デフォルトの`num_threads`の値は`1 + 2x(利用可能なCPU数)`になります。 + +したがって、このモードで`num_threads`と`num`が明示的に設定されている場合、`num_threads`は`num`よりも厳密に大きくする必要があります。 + ## スーパーグローバルの動作 [PHPのスーパーグローバル](https://www.php.net/manual/language.variables.superglobals.php)(`$_SERVER`、`$_ENV`、`$_GET`など) @@ -187,3 +228,39 @@ $handler = static function () use ($workerServer) { // ... ``` + +ほとんどのスーパーグローバル(`$_GET`、`$_POST`、`$_COOKIE`、`$_FILES`、`$_SERVER`、`$_REQUEST`)はリクエスト間で自動的にリセットされます。 +しかし、**`$_ENV`は現在、リクエスト間でリセットされません**。 +これは、リクエスト中に`$_ENV`に加えられた変更が永続し、同じワーカーのスレッドで処理される後続のリクエストに表示されることを意味します。 +リクエスト固有のデータや機密データを`$_ENV`に保存することは避けてください。 + +## 状態の永続性 + +ワーカーモードではPHPプロセスがリクエスト間で起動状態を保つため、以下の状態がリクエスト間で永続します: + +- **静的変数**: 関数やメソッド内で`static`として宣言された変数は、リクエスト間でその値を保持します。 +- **クラスの静的プロパティ**: クラスの静的プロパティはリクエスト間で永続します。 +- **グローバル変数**: ワーカー スクリプトのグローバルスコープ内の変数はリクエスト間で永続します。 +- **インメモリキャッシュ**: リクエストハンドラーの外部にメモリ(配列、オブジェクト)に保存されたデータは永続します。 +- **ランタイムエンジン設定**: `ini_set()`、`stream_context_set_default()`、`date_default_timezone_set()`、`chdir()`、`stream_wrapper_register()`、`set_error_handler()`、または`set_exception_handler()`で行われた変更は、それ以降のリクエストでも有効なままになります。デフォルトのストリームコンテキストに資格情報などのリクエスト固有またはユーザー固有の値を保存しないでください。代わりに、各呼び出しに明示的なコンテキストを渡してください。 + +これは意図された設計であり、ワーカーモードを高速にする要因です。ただし、意図しない副作用を避けるためには注意が必要です: + +```php + [!TIP] -> A seção a seguir é necessária apenas antes do Symfony 7.4, onde o suporte nativo para o modo worker do FrankenPHP foi introduzido. - -O modo worker do FrankenPHP é suportado pelo [Componente Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). -Para iniciar qualquer aplicação Symfony em um worker, instale o pacote FrankenPHP do [PHP Runtime](https://github.com/php-runtime/runtime): - -```console -composer require runtime/frankenphp-symfony -``` +## Modo worker para Symfony -Inicie seu servidor de aplicações definindo a variável de ambiente `APP_RUNTIME` para usar o Symfony Runtime do FrankenPHP: +Consulte [a documentação do modo worker do Symfony com FrankenPHP](symfony.md#symfony-worker-mode-with-frankenphp). -```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 -``` +## Modo worker para Laravel Octane -## Laravel Octane +Consulte [a documentação do Laravel Octane para FrankenPHP](laravel.md#laravel-octane). -Consulte [a documentação dedicada](laravel.md#laravel-octane). +## Escrevendo um script de worker FrankenPHP personalizado -## Aplicações personalizadas - -O exemplo a seguir mostra como criar seu próprio worker script sem depender de uma biblioteca de terceiros: +O exemplo a seguir mostra como criar seu próprio script de worker sem depender de uma biblioteca de terceiros: ```php boot(); -// Manipulador fora do loop para melhor desempenho (fazendo menos trabalho) +// Handler outside the loop for better performance (doing less work) $handler = static function () use ($myApp) { try { - // Chamado quando uma requisição é recebida, - // superglobais, php://input e similares são redefinidos + // Called when a request is received, + // superglobals, php://input and the like are reset echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); } catch (\Throwable $exception) { - // `set_exception_handler` é chamado apenas quando o worker script termina, - // o que pode não ser o que você espera, então capture e trate exceções aqui + // `set_exception_handler` is called only when the worker script ends, + // which may not be what you expect, so catch and handle exceptions here (new \MyCustomExceptionHandler)->handleException($exception); } }; @@ -95,22 +81,22 @@ $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { $keepRunning = \frankenphp_handle_request($handler); - // Faz algo depois de enviar a resposta HTTP + // Do something after sending the HTTP response $myApp->terminate(); - // Chama o coletor de lixo para reduzir as chances de ele ser acionado no meio da geração de uma página + // Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation gc_collect_cycles(); if (!$keepRunning) break; } -// Limpeza +// Cleanup $myApp->shutdown(); ``` Em seguida, inicie sua aplicação e use a variável de ambiente `FRANKENPHP_CONFIG` para configurar seu worker: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -v $PWD:/app \ @@ -121,7 +107,7 @@ docker run \ Por padrão, são iniciados 2 workers por CPU. Você também pode configurar o número de workers a serem iniciados: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php 42" \ -v $PWD:/app \ @@ -129,26 +115,78 @@ docker run \ dunglas/frankenphp ``` +### Usando PSR-15 + +Se sua aplicação usa [PSR-15](https://www.php-fig.org/psr/psr-15/) em vez de superglobais, converta a requisição/resposta nas bordas do manipulador com [`nyholm/psr7`](https://github.com/Nyholm/psr7) e [`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server): + +```console +composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler +``` + +```php +boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### Reiniciar o worker após um certo número de requisições -Como o PHP não foi originalmente projetado para processos de longa duração, ainda existem muitas bibliotecas e códigos legados que vazam memória. -Uma solução alternativa para usar esse tipo de código no modo worker é reiniciar o worker script após processar um certo número de requisições: +Como o PHP não foi originalmente projetado para processos de longa duração, muitas bibliotecas e códigos legados ainda vazam memória. +Uma solução alternativa para usar esse tipo de código no modo worker é reiniciar o script de worker após processar um certo número de requisições: O trecho de código de worker anterior permite configurar um número máximo de requisições a serem processadas, definindo uma variável de ambiente chamada `MAX_REQUESTS`. ### Reiniciar os workers manualmente -Embora seja possível reiniciar os workers [em alterações de arquivo](config.md#watching-for-file-changes), também é possível reiniciar todos os workers graciosamente por meio da [API de administração do Caddy](https://caddyserver.com/docs/api). Se o administrador estiver habilitado no seu [Caddyfile](config.md#caddyfile-config), você pode acionar o endpoint de reinicialização com uma simples requisição POST como esta: +Embora seja possível reiniciar os workers [em alterações de arquivo](config.md#watching-for-file-changes), também é possível reiniciar todos os workers +graciosamente por meio da [API de administração do Caddy](https://caddyserver.com/docs/api). Se o administrador estiver habilitado no seu +[Caddyfile](config.md#caddyfile-config), você pode acionar o endpoint de reinicialização com uma simples requisição POST como esta: -```console +```bash curl -X POST http://localhost:2019/frankenphp/workers/restart ``` ### Falhas de worker -Se um worker script travar com um código de saída diferente de zero, o FrankenPHP o reiniciará com uma estratégia de backoff exponencial. -Se o worker script permanecer ativo por mais tempo do que o último backoff × 2, ele não irá penalizar o worker script e reiniciá-lo novamente. -No entanto, se o worker script continuar a falhar com um código de saída diferente de zero em um curto período de tempo (por exemplo, com um erro de digitação em um script), o FrankenPHP travará com o erro: `too many consecutive failures`. +Se um script de worker travar com um código de saída diferente de zero, o FrankenPHP o reiniciará com uma estratégia de backoff exponencial. +Se o script de worker permanecer ativo por mais tempo do que o último backoff × 2, +ele não irá penalizar o script de worker e o reiniciará novamente. +No entanto, se o script de worker continuar a falhar com um código de saída diferente de zero em um curto período de tempo +(por exemplo, com um erro de digitação em um script), o FrankenPHP travará com o erro: `too many consecutive failures`. O número de falhas consecutivas pode ser configurado no seu [Caddyfile](config.md#caddyfile-config) com a opção `max_consecutive_failures`: @@ -161,25 +199,67 @@ frankenphp { } ``` +### Número de threads + +Quando o modo worker está habilitado, um thread adicional é necessário para lidar com as requisições recebidas, +portanto, o valor padrão de `num_threads` é `1 + 2x(número de CPUs disponíveis)`. +Assim, nesse modo, se `num_threads` e `num` forem definidos explicitamente, `num_threads` deve ser estritamente maior que `num`. + ## Comportamento das superglobais -As [superglobais do PHP](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) se comportam da seguinte maneira: +As [superglobais do PHP](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) +se comportam da seguinte maneira: -- antes da primeira chamada para `frankenphp_handle_request()`, as superglobais contêm valores vinculados ao próprio worker script. -- durante e após a chamada para `frankenphp_handle_request()`, as superglobais contêm valores gerados a partir da requisição HTTP processada. Cada chamada para `frankenphp_handle_request()` altera os valores das superglobais. +- antes da primeira chamada para `frankenphp_handle_request()`, as superglobais contêm valores vinculados ao próprio script de worker +- durante e após a chamada para `frankenphp_handle_request()`, as superglobais contêm valores gerados a partir da requisição HTTP processada, cada chamada para `frankenphp_handle_request()` altera os valores das superglobais -Para acessar as superglobais do worker script dentro do retorno de chamada, você deve copiá-las e importar a cópia para o escopo do retorno de chamada: +Para acessar as superglobais do script de worker dentro do retorno de chamada, você deve copiá-las e importar a cópia para o escopo do retorno de chamada: ```php [!TIP] -> Следующий раздел актуален только для версий Symfony до 7.4, где была введена нативная поддержка режима воркеров FrankenPHP. - -Worker режим FrankenPHP поддерживается компонентом [Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). -Чтобы запустить любое Symfony-приложение в worker режиме, установите пакет FrankenPHP для [PHP Runtime](https://github.com/php-runtime/runtime): - -```console -composer require runtime/frankenphp-symfony -``` - -Запустите сервер приложения, задав переменную окружения `APP_RUNTIME` для использования FrankenPHP Symfony Runtime: +## Worker-режим для 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 -``` +См. [документацию по worker-режиму FrankenPHP для Symfony](symfony.md#symfony-worker-mode-with-frankenphp). -## Laravel Octane +## Worker-режим для Laravel Octane -Подробнее см. в [отдельной документации](laravel.md#laravel-octane). +См. [документацию по Laravel Octane для FrankenPHP](laravel.md#laravel-octane). -## Пользовательские приложения +## Написание пользовательского worker-скрипта FrankenPHP Следующий пример показывает, как создать собственный worker-скрипт без использования сторонних библиотек: @@ -110,7 +96,7 @@ $myApp->shutdown(); Запустите ваше приложение и используйте переменную окружения `FRANKENPHP_CONFIG` для настройки вашего воркера: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -v $PWD:/app \ @@ -120,7 +106,7 @@ docker run \ По умолчанию запускается 2 воркера на каждый CPU. Вы также можете настроить количество запускаемых воркеров: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php 42" \ -v $PWD:/app \ @@ -128,6 +114,54 @@ docker run \ dunglas/frankenphp ``` +### Использование PSR-15 + +Если ваше приложение использует [PSR-15](https://www.php-fig.org/psr/psr-15/) вместо суперглобальных переменных, преобразуйте запрос/ответ на границах обработчика с помощью [`nyholm/psr7`](https://github.com/Nyholm/psr7) и [`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server): + +```console +composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler +``` + +```php +boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### Перезапуск worker-скрипта после определённого количества запросов PHP изначально не предназначался для долгоживущих процессов, поэтому есть ещё много библиотек и устаревшего кода, которые вызывают утечки памяти. @@ -139,7 +173,7 @@ PHP изначально не предназначался для долгожи Хотя можно перезапускать воркеры [при изменении файлов](config.md#watching-for-file-changes), также можно корректно перезапустить все воркеры через [API администрирования Caddy](https://caddyserver.com/docs/api). Если администрирование включено в вашем [Caddyfile](config.md#caddyfile-config), вы можете отправить запрос POST на конечную точку перезапуска следующим образом: -```console +```bash curl -X POST http://localhost:2019/frankenphp/workers/restart ``` @@ -161,6 +195,12 @@ frankenphp { } ``` +### Количество потоков + +Когда включен режим воркера, требуется дополнительный поток для обработки входящих запросов, поэтому значение по умолчанию для `num_threads` составляет `1 + 2x(количество доступных CPU)`. + +Таким образом, в этом режиме, если `num_threads` и `num` заданы явно, `num_threads` должно быть строго больше, чем `num`. + ## Поведение суперглобальных переменных [PHP суперглобальные переменные](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) ведут себя следующим образом: @@ -182,3 +222,35 @@ $handler = static function () use ($workerServer) { // ... ``` + +Большинство суперглобальных переменных (`$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `$_SERVER`, `$_REQUEST`) автоматически сбрасываются между запросами. Однако, **`$_ENV` в настоящее время не сбрасывается между запросами**. Это означает, что любые изменения, внесённые в `$_ENV` во время запроса, будут сохраняться и быть видны последующим запросам, обрабатываемым тем же потоком воркера. Избегайте хранения данных, специфичных для запроса или конфиденциальных, в `$_ENV`. + +## Сохранение состояния + +Поскольку режим воркера сохраняет PHP-процесс активным между запросами, следующее состояние сохраняется между запросами: + +- **Статические переменные**: Переменные, объявленные с `static` внутри функций или методов, сохраняют свои значения между запросами. +- **Статические свойства классов**: Статические свойства классов сохраняются между запросами. +- **Глобальные переменные**: Переменные в глобальной области видимости worker-скрипта сохраняются между запросами. +- **Кэши в памяти**: Любые данные, хранящиеся в памяти (массивы, объекты) за пределами обработчика запросов, сохраняются. +- **Настройки среды выполнения**: Изменения, сделанные с помощью `ini_set()`, `stream_context_set_default()`, `date_default_timezone_set()`, `chdir()`, `stream_wrapper_register()`, `set_error_handler()` или `set_exception_handler()`, остаются в силе для последующих запросов. Никогда не храните там значения, специфичные для запроса или пользователя, например, учетные данные в контексте потока по умолчанию: вместо этого передавайте явный контекст при каждом вызове. + +Это сделано намеренно и является тем, что делает режим воркера быстрым. Однако это требует внимания, чтобы избежать непредвиденных побочных эффектов: + +```php + [!TIP] -> Bu bölüm, FrankenPHP worker moduna yerel desteğin sunulduğu Symfony 7.4 öncesi için gereklidir. - -FrankenPHP'nin worker modu [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) tarafından desteklenmektedir. -Herhangi bir Symfony uygulamasını bir worker'da başlatmak için [PHP Runtime](https://github.com/php-runtime/runtime)'ın FrankenPHP paketini yükleyin: - -```console -composer require runtime/frankenphp-symfony -``` - -FrankenPHP Symfony Runtime'ı kullanmak için `APP_RUNTIME` ortam değişkenini tanımlayarak uygulama sunucunuzu başlatın: +## Symfony için Worker Modu -```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 -``` +Bkz. [FrankenPHP Symfony worker modu dokümantasyonu](symfony.md#symfony-worker-mode-with-frankenphp). -## Laravel Octane +## Laravel Octane için Worker Modu -Bkz. [özel dokümantasyon](laravel.md#laravel-octane). +Bkz. [FrankenPHP Laravel Octane dokümantasyonu](laravel.md#laravel-octane). -## Özel uygulamalar +## Özel bir FrankenPHP worker betiği yazma Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi worker betiğinizi nasıl oluşturacağınızı göstermektedir: @@ -110,7 +96,7 @@ $myApp->shutdown(); Ardından, uygulamanızı başlatın ve worker'ınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -v $PWD:/app \ @@ -121,7 +107,7 @@ docker run \ Varsayılan olarak, CPU başına 2 worker başlatılır. Başlatılacak worker sayısını da yapılandırabilirsiniz: -```console +```bash docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php 42" \ -v $PWD:/app \ @@ -129,6 +115,54 @@ docker run \ dunglas/frankenphp ``` +### PSR-15 Kullanma + +Uygulamanız süper küresel değişkenler yerine [PSR-15](https://www.php-fig.org/psr/psr-15/) ile iletişim kuruyorsa, işleyicinin kenarlarındaki istek/yanıtı [nyholm/psr7](https://github.com/Nyholm/psr7) ve [nyholm/psr7-server](https://github.com/Nyholm/psr7-server) ile dönüştürün: + +```console +composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler +``` + +```php +boot(); + +$psr17Factory = new Psr17Factory(); +$creator = new ServerRequestCreator( + $psr17Factory, // ServerRequestFactory + $psr17Factory, // UriFactory + $psr17Factory, // UploadedFileFactory + $psr17Factory, // StreamFactory +); + +$handler = static function () use ($myApp, $creator) { + $response = $myApp->handle($creator->fromGlobals()); + + http_response_code($response->getStatusCode()); + foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } + } + echo $response->getBody(); +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = \frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) break; +} +``` + ### Belirli sayıda istekten sonra worker'ı yeniden başlatın PHP başlangıçta uzun süreli işlemler için tasarlanmadığından, hala bellek sızdıran birçok kütüphane ve eski kod vardır. @@ -138,15 +172,21 @@ Bu tür kodları worker modunda kullanmak için geçici bir çözüm, belirli sa ### Worker'ları manuel olarak yeniden başlatma -Worker'ları [dosya değişikliklerinde](config.md#watching-for-file-changes) yeniden başlatmak mümkünken, tüm worker'ları [Caddy admin API](https://caddyserver.com/docs/api) aracılığıyla sorunsuz bir şekilde yeniden başlatmak da mümkündür. Yönetici [Caddyfile](config.md#caddyfile-config)'ınızda etkinleştirilmişse, yeniden başlatma uç noktasına aşağıdaki gibi basit bir POST isteği gönderebilirsiniz: +Worker'ları [dosya değişikliklerinde](config.md#watching-for-file-changes) yeniden başlatmak mümkünken, tüm worker'ları +[Caddy admin API](https://caddyserver.com/docs/api) aracılığıyla sorunsuz bir şekilde yeniden başlatmak da mümkündür. Yönetici +[Caddyfile](config.md#caddyfile-config)'ınızda etkinleştirilmişse, yeniden başlatma uç noktasına aşağıdaki gibi basit bir POST isteği gönderebilirsiniz: -```console +```bash curl -X POST http://localhost:2019/frankenphp/workers/restart ``` ### Worker hataları -Bir worker betiği sıfır olmayan bir çıkış koduyla çökerse, FrankenPHP onu üstel bir geri çekilme (exponential backoff) stratejisiyle yeniden başlatacaktır. Worker betiği, son geri çekilme süresinin 2 katından daha uzun süre çalışır durumda kalırsa, worker betiğini cezalandırmayacak ve tekrar yeniden başlatacaktır. Ancak, worker betiği kısa bir süre içinde sıfır olmayan bir çıkış koduyla başarısız olmaya devam ederse (örneğin, bir betikte yazım hatası olması durumunda), FrankenPHP `too many consecutive failures` hatasıyla çökecektir. +Bir worker betiği sıfır olmayan bir çıkış koduyla çökerse, FrankenPHP onu üstel bir geri çekilme (exponential backoff) stratejisiyle yeniden başlatacaktır. +Worker betiği, son geri çekilme süresinin 2 katından daha uzun süre çalışır durumda kalırsa, +worker betiğini cezalandırmayacak ve tekrar yeniden başlatacaktır. +Ancak, worker betiği kısa bir süre içinde sıfır olmayan bir çıkış koduyla başarısız olmaya devam ederse +(örneğin, bir betikte yazım hatası olması durumunda), FrankenPHP `too many consecutive failures` hatasıyla çökecektir. Ardışık hata sayısı, [Caddyfile](config.md#caddyfile-config)'ınızda `max_consecutive_failures` seçeneği ile yapılandırılabilir: @@ -159,9 +199,16 @@ frankenphp { } ``` +### İş parçacığı sayısı + +Worker modu etkinleştirildiğinde, gelen istekleri işlemek için ek bir iş parçacığı gerektiğinden, varsayılan `num_threads` değeri `1 + 2x(kullanılabilir CPU sayısı)` şeklindedir. + +Bu nedenle, bu modda `num_threads` ve `num` açıkça ayarlanmışsa, `num_threads`, `num`'dan kesinlikle büyük olmalıdır. + ## Süper küresel değişkenlerin davranışı -[PHP süper küresel değişkenleri](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) aşağıdaki gibi davranır: +[PHP süper küresel değişkenleri](https://www.php.net/manual/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) +aşağıdaki gibi davranır: - `frankenphp_handle_request()`'e ilk çağrıdan önce, süper küresel değişkenler worker betiğinin kendisine bağlı değerleri içerir - `frankenphp_handle_request()` çağrısı sırasında ve sonrasında, süper küresel değişkenler işlenen HTTP isteğinden üretilen değerleri içerir, `frankenphp_handle_request()`'e yapılan her çağrı süper küresel değişken değerlerini değiştirir @@ -174,9 +221,45 @@ Geri çağırım içinde worker betiğinin süper küresel değişkenlerine eri $workerServer = $_SERVER; $handler = static function () use ($workerServer) { - var_dump($_SERVER); // HTTP isteğine bağlı $_SERVER + var_dump($_SERVER); // İsteğe bağlı $_SERVER var_dump($workerServer); // Worker betiğinin $_SERVER'ı }; // ... ``` + +Çoğu süper küresel değişken (`$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `$_SERVER`, `$_REQUEST`) istekler arasında otomatik olarak sıfırlanır. +Ancak, **`$_ENV` şu anda istekler arasında sıfırlanmamaktadır**. +Bu, bir istek sırasında `$_ENV` üzerinde yapılan herhangi bir değişikliğin kalıcı olacağı ve aynı worker iş parçacığı tarafından işlenen sonraki istekler tarafından görüleceği anlamına gelir. +İsteğe özgü veya hassas verileri `$_ENV` içinde saklamaktan kaçının. + +## Durum Kalıcılığı + +Worker modu, PHP sürecini istekler arasında canlı tuttuğu için, aşağıdaki durum istekler arasında kalıcı olur: + +- **Statik değişkenler**: Fonksiyonlar veya metotlar içinde `static` ile bildirilen değişkenler, istekler arasında değerlerini korur. +- **Sınıf statik özellikleri**: Sınıflardaki statik özellikler, istekler arasında kalıcı olur. +- **Küresel değişkenler**: Worker betiğinin küresel kapsamındaki değişkenler, istekler arasında kalıcı olur. +- **Bellek içi önbellekler**: İstek işleyicisi dışında bellekte depolanan herhangi bir veri (diziler, nesneler) kalıcı olur. +- **Çalışma zamanı motoru ayarları**: `ini_set()`, `stream_context_set_default()`, `date_default_timezone_set()`, `chdir()`, `stream_wrapper_register()`, `set_error_handler()` veya `set_exception_handler()` ile yapılan değişiklikler sonraki istekler için geçerliliğini korur. Varsayılan akış bağlamında kimlik bilgileri gibi isteğe veya kullanıcıya özgü değerleri asla orada saklamayın: bunun yerine her çağrıya açık bir bağlam geçirin. + +Bu, tasarım gereğidir ve worker modunu hızlı yapan şey budur. Ancak, istenmeyen yan etkilerden kaçınmak için dikkat gerektirir: + +```php +