コンテンツへスキップ

Laravel Octane

イントロダクション

Laravel Octaneは、FrankenPHPOpen SwooleSwooleRoadRunnerなどの高性能アプリケーションサーバを使用してアプリケーションを提供することで、そのパフォーマンスを大幅に向上させます。Octaneはアプリケーションを一度起動してメモリ上に保持し、超高速でリクエストを供給します。

インストール

OctaneはComposerパッケージマネージャでインストールできます。

1composer require laravel/octane

Octaneをインストールした後、octane:install Artisanコマンドを実行すると、Octaneの設定ファイルがアプリケーションにインストールされます。

1php artisan octane:install

サーバの必要要件

Laravel OctaneはPHP 8.1+が必要です。

FrankenPHP

FrankenPHPは、Goで書かれたPHPアプリケーションサーバで、Early Hints、Brotli、Zstandard圧縮などの最新のWeb機能をサポートしています。Octaneをインストールし、サーバとしてFrankenPHPを選択すると、Octaneが自動的にFrankenPHPバイナリをダウンロードし、インストールします。

Laravel SailによるFrankenPHP

Laravel Sailを使用してアプリケーションを開発する予定の場合は、次のコマンドを実行してOctaneとFrankenPHPをインストールする必要があります。

1./vendor/bin/sail up
2 
3./vendor/bin/sail composer require laravel/octane

次に、octane:install Artisanコマンドを使用してFrankenPHPバイナリをインストールします。

1./vendor/bin/sail artisan octane:install --server=frankenphp

最後に、アプリケーションのdocker-compose.ymlファイルにあるlaravel.testサービス定義にSUPERVISOR_PHP_COMMAND環境変数を追加してください。この環境変数は、PHP開発サーバの代わりにOctaneを使用してアプリケーションを提供するためにSailが使用するコマンドを格納します。

1services:
2 laravel.test:
3 environment:
4 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='${APP_PORT:-80}'"
5 XDG_CONFIG_HOME: /var/www/html/config
6 XDG_DATA_HOME: /var/www/html/data

HTTPS、HTTP/2、HTTP/3を有効にするには、代わりに以下の変更を適用してください。

1services:
2 laravel.test:
3 ports:
4 - '${APP_PORT:-80}:80'
5 - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
6 - '443:443'
7 - '443:443/udp'
8 environment:
9 SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --host=localhost --port=443 --admin-port=2019 --https"
10 XDG_CONFIG_HOME: /var/www/html/config
11 XDG_DATA_HOME: /var/www/html/data

https://127.0.0.1の使用は追加の設定が必要であり、推奨されていないため、通常、FrankenPHP Sailアプリケーションにはhttps://を介してアクセスする必要があります。

DockerによるFrankenPHP

FrankenPHPの公式Dockerイメージを使用すると、パフォーマンスが向上し、FrankenPHPの静的インストールに含まれていない追加の拡張機能を使用できます。さらに、公式のDockerイメージは、WindowsなどネイティブではサポートしていないプラットフォームでFrankenPHPを実行するためのサポートを提供します。FrankenPHPの公式Dockerイメージは、ローカル開発と本番環境の両方での使用に適しています。

FrankenPHPを利用するLaravelアプリケーションをコンテナ化するための出発点として、以下のDockerfileを使用できます。

1FROM dunglas/frankenphp
2 
3RUN install-php-extensions \
4 pcntl
5 # Add other PHP extensions here...
6 
7COPY . /app
8 
9ENTRYPOINT ["php", "artisan", "octane:frankenphp"]

次に、開発中は、以下のDocker Composeファイルを使用してアプリケーションを実行できます。

1# compose.yaml
2services:
3 frankenphp:
4 build:
5 context: .
6 entrypoint: php artisan octane:frankenphp --workers=1 --max-requests=1
7 ports:
8 - "8000:8000"
9 volumes:
10 - .:/app

--log-levelオプションがphp artisan octane:startコマンドに明示的に渡された場合、OctaneはFrankenPHPのネイティブロガーを使用し、特に設定されていない限り、構造化されたJSONログを生成します。

DockerでFrankenPHPを実行する方法の詳細については、公式のFrankenPHPドキュメントを参照してください。

RoadRunner

RoadRunnerは、Goで構築されたRoadRunnerバイナリによって動作します。RoadRunnerベースのOctaneサーバを初めて起動すると、OctaneはRoadRunnerバイナリのダウンロードとインストールを提案します。

Laravel SailによるRoadRunner

Laravel Sailを使用してアプリケーションを開発する予定の場合は、次のコマンドを実行してOctaneとRoadRunnerをインストールする必要があります。

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

次に、Sailシェルを起動し、rr実行可能ファイルを使用して、RoadRunnerバイナリの最新のLinuxベースのビルドを取得する必要があります。

1./vendor/bin/sail shell
2 
3# Within the Sail shell...
4./vendor/bin/rr get-binary

次に、アプリケーションのdocker-compose.ymlファイルにあるlaravel.testサービス定義にSUPERVISOR_PHP_COMMAND環境変数を追加してください。この環境変数は、PHP開発サーバの代わりにOctaneを使用してアプリケーションを提供するためにSailが使用するコマンドを格納します。

1services:
2 laravel.test:
3 environment:
4 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='${APP_PORT:-80}'"

最後に、rrバイナリが実行可能であることを確認し、Sailイメージをビルドします。

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

Swoole

Swooleアプリケーションサーバを使用してLaravel Octaneアプリケーションを提供する場合、Swoole PHP拡張機能をインストールする必要があります。通常、これはPECLを介して行います。

1pecl install swoole

Open Swoole

Open Swooleアプリケーションサーバを使用してLaravel Octaneアプリケーションを提供する場合、Open Swoole PHP拡張機能をインストールする必要があります。通常、これはPECLを介して行います。

1pecl install openswoole

Laravel OctaneでOpen Swooleを使用すると、並行タスク、Tick、Intervalなど、Swooleが提供するのと同じ機能が利用できます。

Laravel SailによるSwoole

Sail経由でOctaneアプリケーションを提供する前に、Laravel Sailの最新バージョンがあることを確認し、アプリケーションのルートディレクトリ内で./vendor/bin/sail build --no-cacheを実行してください。

あるいは、Laravelの公式Dockerベース開発環境であるLaravel Sailを使用して、SwooleベースのOctaneアプリケーションを開発することもできます。Laravel SailにはデフォルトでSwoole拡張機能が含まれています。ただし、Sailが使用するdocker-compose.ymlファイルを調整する必要はあります。

まず、アプリケーションのdocker-compose.ymlファイルにあるlaravel.testサービス定義にSUPERVISOR_PHP_COMMAND環境変数を追加してください。この環境変数は、PHP開発サーバの代わりにOctaneを使用してアプリケーションを提供するためにSailが使用するコマンドを格納します。

1services:
2 laravel.test:
3 environment:
4 SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port='${APP_PORT:-80}'"

最後に、Sailイメージをビルドします。

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

Swooleの設定

Swooleは、必要に応じてoctane設定ファイルに追加できる、いくつかの追加設定オプションをサポートしています。これらはめったに変更する必要がないため、デフォルトの設定ファイルには含まれていません。

1'swoole' => [
2 'options' => [
3 'log_file' => storage_path('logs/swoole_http.log'),
4 'package_max_length' => 10 * 1024 * 1024,
5 ],
6],

アプリケーションの実行

Octaneサーバはoctane:start Artisanコマンドで起動できます。デフォルトでは、このコマンドは、アプリケーションのoctane設定ファイルのserver設定オプションで指定されたサーバを利用します。

1php artisan octane:start

デフォルトで、Octaneはポート8000でサーバを起動するため、Webブラウザでhttps://:8000経由でアプリケーションにアクセスできます。

HTTPSによるアプリケーションの実行

デフォルトでは、Octane経由で実行されるアプリケーションは、http://で始まるリンクを生成します。アプリケーションのconfig/octane.php設定ファイル内で使用されるOCTANE_HTTPS環境変数は、HTTPS経由でアプリケーションを提供する場合にtrueに設定できます。この設定値がtrueに設定されている場合、OctaneはLaravelに対し、生成されるすべてのリンクにhttps://を付けるように指示します。

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

Nginxによるアプリケーションの実行

独自のサーバ設定を管理する準備がまだできていない場合や、堅牢なLaravel Octaneアプリケーションを実行するために必要な様々なサービスすべての設定に慣れていない場合は、フルマネージドのLaravel Octaneサポートを提供するLaravel Cloudをチェックしてください。

本番環境では、OctaneアプリケーションをNginxやApacheなどの従来のWebサーバの背後で提供する必要があります。そうすることで、Webサーバが画像やスタイルシートなどの静的アセットを提供したり、SSL証明書の終端を管理したりできるようになります。

以下のNginx設定例では、Nginxがサイトの静的アセットを提供し、ポート8000で実行されているOctaneサーバにリクエストをプロキシします。

1map $http_upgrade $connection_upgrade {
2 default upgrade;
3 '' close;
4}
5 
6server {
7 listen 80;
8 listen [::]:80;
9 server_name domain.com;
10 server_tokens off;
11 root /home/forge/domain.com/public;
12 
13 index index.php;
14 
15 charset utf-8;
16 
17 location /index.php {
18 try_files /not_exists @octane;
19 }
20 
21 location / {
22 try_files $uri $uri/ @octane;
23 }
24 
25 location = /favicon.ico { access_log off; log_not_found off; }
26 location = /robots.txt { access_log off; log_not_found off; }
27 
28 access_log off;
29 error_log /var/log/nginx/domain.com-error.log error;
30 
31 error_page 404 /index.php;
32 
33 location @octane {
34 set $suffix "";
35 
36 if ($uri = /index.php) {
37 set $suffix ?$query_string;
38 }
39 
40 proxy_http_version 1.1;
41 proxy_set_header Host $http_host;
42 proxy_set_header Scheme $scheme;
43 proxy_set_header SERVER_PORT $server_port;
44 proxy_set_header REMOTE_ADDR $remote_addr;
45 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
46 proxy_set_header Upgrade $http_upgrade;
47 proxy_set_header Connection $connection_upgrade;
48 
49 proxy_pass http://127.0.0.1:8000$suffix;
50 }
51}

ファイル変更の監視

Octaneサーバの起動時にアプリケーションは一度メモリにロードされるため、アプリケーションのファイルを変更しても、ブラウザを更新しただけでは反映されません。たとえば、routes/web.phpファイルに追加したルート定義は、サーバを再起動するまで反映されません。便宜上、--watchフラグを使用して、アプリケーション内のファイルが変更されたときにサーバを自動的に再起動するようにOctaneに指示できます。

1php artisan octane:start --watch

この機能を使用する前に、ローカル開発環境にNodeがインストールされていることを確認する必要があります。さらに、Chokidarファイル監視ライブラリをプロジェクトにインストールする必要があります。

1npm install --save-dev chokidar

アプリケーションのconfig/octane.php設定ファイル内のwatch設定オプションを使用して、監視対象のディレクトリとファイルを設定できます。

ワーカ数の指定

デフォルトでは、Octaneはマシンが提供する各CPUコアに対してアプリケーションリクエストワーカを1つ起動します。これらのワーカは、アプリケーションに入ってくるHTTPリクエストを処理するために使用されます。octane:startコマンドを呼び出すときに--workersオプションを使用して、起動するワーカの数を手動で指定できます。

1php artisan octane:start --workers=4

Swooleアプリケーションサーバを使用している場合は、起動したい「タスクワーカ」の数を指定することもできます。

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

最大リクエスト数の指定

メモリリークの発生を防ぐため、Octaneはワーカが500リクエストを処理すると、そのワーカを正常に再起動します。この数を調整するには、--max-requestsオプションを使用します。

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

ワーカのリロード

octane:reloadコマンドを使用して、Octaneサーバのアプリケーションワーカを正常に再起動できます。通常、これはデプロイ後に行い、新しくデプロイしたコードをメモリにロードし、後続のリクエストに提供できるようにします。

1php artisan octane:reload

サーバの停止

octane:stop Artisanコマンドを使用して、Octaneサーバを停止できます。

1php artisan octane:stop

サーバステータスの確認

octane:status Artisanコマンドを使用して、Octaneサーバの現在のステータスを確認できます。

1php artisan octane:status

依存注入とOctane

Octaneはアプリケーションを一度起動し、リクエストを処理している間メモリに保持するため、アプリケーションを構築する際には考慮すべき注意点がいくつかあります。たとえば、アプリケーションのサービスプロバイダのregisterメソッドとbootメソッドは、リクエストワーカが最初に起動するときに一度だけ実行されます。後続のリクエストでは、同じアプリケーションインスタンスが再利用されます。

これを考慮すると、アプリケーションサービスコンテナやリクエストをオブジェクトのコンストラクタに注入する際には、特に注意を払う必要があります。そうすることで、そのオブジェクトは後続のリクエストで古いバージョンのコンテナやリクエストを持つ可能性があります。

Octaneは、リクエスト間でファーストパーティのフレームワークの状態をリセットする処理を自動的に行います。しかし、Octaneはアプリケーションによって作成されたグローバルな状態をリセットする方法を常に知っているわけではありません。したがって、Octaneフレンドリーな方法でアプリケーションを構築する方法を認識しておく必要があります。以下では、Octaneを使用する際に問題を引き起こす可能性のある最も一般的な状況について説明します。

コンテナの注入

一般的に、アプリケーションサービスコンテナやHTTPリクエストインスタンスを他のオブジェクトのコンストラクタに注入することは避けるべきです。たとえば、次のバインディングは、シングルトンとしてバインドされているオブジェクトにアプリケーションサービスコンテナ全体を注入します。

1use App\Service;
2use Illuminate\Contracts\Foundation\Application;
3 
4/**
5 * Register any application services.
6 */
7public function register(): void
8{
9 $this->app->singleton(Service::class, function (Application $app) {
10 return new Service($app);
11 });
12}

この例では、アプリケーションの起動プロセス中にServiceインスタンスが解決されると、コンテナがサービスに注入され、後続のリクエストでもその同じコンテナがServiceインスタンスによって保持されます。これは、特定のアプリケーションでは問題にならないかもしれませんが、起動サイクルの後半や後続のリクエストによって追加されたバインディングがコンテナに予期せず欠落する原因となる可能性があります。

回避策として、バインディングをシングルトンとして登録するのをやめるか、常に現在のコンテナインスタンスを解決するコンテナリゾルバクロージャをサービスに注入することができます。

1use App\Service;
2use Illuminate\Container\Container;
3use Illuminate\Contracts\Foundation\Application;
4 
5$this->app->bind(Service::class, function (Application $app) {
6 return new Service($app);
7});
8 
9$this->app->singleton(Service::class, function () {
10 return new Service(fn () => Container::getInstance());
11});

グローバルなappヘルパとContainer::getInstance()メソッドは、常に最新バージョンのアプリケーションコンテナを返します。

リクエストの注入

一般的に、アプリケーションサービスコンテナやHTTPリクエストインスタンスを他のオブジェクトのコンストラクタに注入することは避けるべきです。たとえば、次のバインディングは、シングルトンとしてバインドされているオブジェクトにリクエストインスタンス全体を注入します。

1use App\Service;
2use Illuminate\Contracts\Foundation\Application;
3 
4/**
5 * Register any application services.
6 */
7public function register(): void
8{
9 $this->app->singleton(Service::class, function (Application $app) {
10 return new Service($app['request']);
11 });
12}

この例では、アプリケーションの起動プロセス中にServiceインスタンスが解決されると、HTTPリクエストがサービスに注入され、後続のリクエストでもその同じリクエストがServiceインスタンスによって保持されます。したがって、すべてのヘッダ、入力、クエリ文字列データ、およびその他すべてのリクエストデータが不正になります。

回避策として、バインディングをシングルトンとして登録するのをやめるか、常に現在のリクエストインスタンスを解決するリクエストリゾルバクロージャをサービスに注入することができます。または、最も推奨されるアプローチは、オブジェクトが必要とする特定のリクエスト情報を実行時にオブジェクトのメソッドの1つに渡すことです。

1use App\Service;
2use Illuminate\Contracts\Foundation\Application;
3 
4$this->app->bind(Service::class, function (Application $app) {
5 return new Service($app['request']);
6});
7 
8$this->app->singleton(Service::class, function (Application $app) {
9 return new Service(fn () => $app['request']);
10});
11 
12// Or...
13 
14$service->method($request->input('name'));

グローバルなrequestヘルパは、アプリケーションが現在処理しているリクエストを常に返すため、アプリケーション内で安全に使用できます。

コントローラメソッドやルートクロージャでIlluminate\Http\Requestインスタンスをタイプヒントすることは問題ありません。

設定リポジトリの注入

一般的に、設定リポジトリインスタンスを他のオブジェクトのコンストラクタに注入することは避けるべきです。たとえば、次のバインディングは、シングルトンとしてバインドされているオブジェクトに設定リポジトリを注入します。

1use App\Service;
2use Illuminate\Contracts\Foundation\Application;
3 
4/**
5 * Register any application services.
6 */
7public function register(): void
8{
9 $this->app->singleton(Service::class, function (Application $app) {
10 return new Service($app->make('config'));
11 });
12}

この例では、リクエスト間で設定値が変更された場合、そのサービスは元のリポジトリインスタンスに依存しているため、新しい値にアクセスできません。

回避策として、バインディングをシングルトンとして登録するのをやめるか、設定リポジトリリゾルバクロージャをクラスに注入することができます。

1use App\Service;
2use Illuminate\Container\Container;
3use Illuminate\Contracts\Foundation\Application;
4 
5$this->app->bind(Service::class, function (Application $app) {
6 return new Service($app->make('config'));
7});
8 
9$this->app->singleton(Service::class, function () {
10 return new Service(fn () => Container::getInstance()->make('config'));
11});

グローバルなconfigは、常に最新バージョンの設定リポジトリを返すため、アプリケーション内で安全に使用できます。

メモリリークの管理

Octaneはリクエスト間でアプリケーションをメモリに保持することを忘れないでください。したがって、静的に維持される配列にデータを追加すると、メモリリークが発生します。たとえば、次のコントローラにはメモリリークがあります。アプリケーションへの各リクエストが静的な$data配列にデータを追加し続けるためです。

1use App\Service;
2use Illuminate\Http\Request;
3use Illuminate\Support\Str;
4 
5/**
6 * Handle an incoming request.
7 */
8public function index(Request $request): array
9{
10 Service::$data[] = Str::random(10);
11 
12 return [
13 // ...
14 ];
15}

アプリケーションを構築する際には、このような種類のメモリリークを作成しないように特に注意を払う必要があります。アプリケーションに新しいメモリリークを導入していないことを確認するために、ローカル開発中にアプリケーションのメモリ使用量を監視することをお勧めします。

並行タスク

この機能にはSwooleが必要です。

Swooleを使用する場合、軽量なバックグラウンドタスクを介して操作を並行して実行できます。これは、Octaneのconcurrentlyメソッドを使用して実現できます。このメソッドをPHPの配列分割束縛と組み合わせることで、各操作の結果を取得できます。

1use App\Models\User;
2use App\Models\Server;
3use Laravel\Octane\Facades\Octane;
4 
5[$users, $servers] = Octane::concurrently([
6 fn () => User::all(),
7 fn () => Server::all(),
8]);

Octaneによって処理される並行タスクは、Swooleの「タスクワーカ」を利用し、受信リクエストとはまったく異なるプロセスで実行されます。並行タスクを処理するために利用できるワーカの数は、octane:startコマンドの--task-workersディレクティブによって決定されます。

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

concurrentlyメソッドを呼び出すときは、Swooleのタスクシステムによって課される制限のため、1024を超えるタスクを提供しないでください。

TickとInterval

この機能にはSwooleが必要です。

Swooleを使用する場合、指定された秒数ごとに実行される「tick」操作を登録できます。`tick`メソッドを介して「tick」コールバックを登録できます。`tick`メソッドに提供される最初の引数は、ティッカーの名前を表す文字列である必要があります。2番目の引数は、指定された間隔で呼び出されるcallableである必要があります。

この例では、10秒ごとに呼び出されるクロージャを登録します。通常、`tick`メソッドは、アプリケーションのサービスプロバイダの1つの`boot`メソッド内で呼び出す必要があります。

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

immediateメソッドを使用すると、Octaneサーバが最初に起動したとき、およびその後N秒ごとにtickコールバックを即座に呼び出すようにOctaneに指示できます。

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

Octaneキャッシュ

この機能にはSwooleが必要です。

Swooleを使用する場合、Octaneキャッシュドライバを利用できます。これは、毎秒最大200万回の操作の読み取りおよび書き込み速度を提供します。したがって、このキャッシュドライバは、キャッシュレイヤーから極端な読み取り/書き込み速度を必要とするアプリケーションにとって優れた選択肢です。

このキャッシュドライバはSwooleテーブルによって動作します。キャッシュに保存されているすべてのデータは、サーバ上のすべてのワーカで利用可能です。ただし、キャッシュされたデータは、サーバが再起動されるとフラッシュされます。

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

Octaneキャッシュで許可されるエントリの最大数は、アプリケーションのoctane設定ファイルで定義できます。

キャッシュの間隔

Laravelのキャッシュシステムが提供する一般的なメソッドに加えて、Octaneキャッシュドライバはインターバルベースのキャッシュを備えています。これらのキャッシュは、指定された間隔で自動的に更新され、アプリケーションのサービスプロバイダの1つのbootメソッド内で登録する必要があります。たとえば、次のキャッシュは5秒ごとに更新されます。

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

テーブル

この機能にはSwooleが必要です。

Swooleを使用する場合、独自の任意のSwooleテーブルを定義して操作できます。Swooleテーブルは非常に高いパフォーマンスのスループットを提供し、これらのテーブル内のデータはサーバ上のすべてのワーカからアクセスできます。ただし、その中のデータはサーバが再起動されると失われます。

テーブルは、アプリケーションのoctane設定ファイルのtables設定配列内で定義する必要があります。最大1000行を許可するテーブルの例がすでに設定されています。文字列カラムの最大サイズは、以下のようにカラムタイプの後にカラムサイズを指定することで設定できます。

1'tables' => [
2 'example:1000' => [
3 'name' => 'string:1000',
4 'votes' => 'int',
5 ],
6],

テーブルにアクセスするには、Octane::tableメソッドを使用します。

1use Laravel\Octane\Facades\Octane;
2 
3Octane::table('example')->set('uuid', [
4 'name' => 'Nuno Maduro',
5 'votes' => 1000,
6]);
7 
8return Octane::table('example')->get('uuid');

Swooleテーブルでサポートされているカラムタイプは、stringint、およびfloatです。

Laravelは最も生産的な方法です
ソフトウェアを構築、デプロイ、監視します。