Laravel Octaneでアプリケーションのパフォーマンスを向上させる
Laravel Octane
ご存知の通り、従来のLaravelアプリケーションでは、PHPは1回のリクエストを1回ずつ処理することしかできませんが、Laravel Octaneを使用すると、複数のリクエストを同時に処理できるようになり、ウェブサイトの速度が向上します。
1. Laravel Octaneとは?
Laravel Octaneは、Laravelアプリケーションのパフォーマンスを向上させるために作成されたオープンソースのパッケージです。最初にアプリケーションを1回だけ起動し、それをメモリ(RAM)に保持し、その後のリクエストは、アプリケーションを最初から再起動するのではなく、メモリ内で保存された状態を再利用して処理されます。
Laravelのリクエストライフサイクル
Laravel Octaneのリクエストライフサイクル
Laravel Octaneのもう一つの特徴は、複数のワーカーを同時に使用してリクエストを処理できることです。これにより、以前のようにリクエストを1回ずつ処理するのではなく、複数のリクエストを同時に処理できるようになります。
Laravel Octaneのリクエスト処理の概要
Octaneは、FrankenPHP、Swoole、RoadRunnerという3つのPHPの非同期処理ツールを基盤として開発されています。この記事では、Swooleに焦点を当てます。
PHP Swooleは、Erlang、Node.js、Nettyの原則を基にPHP向けに設計されています。しかし、SwooleはLinuxカーネル上でのみ動作するため、現在はLinux、OS X、Cygwin、またはWSLでのみ使用できます。
2. PHP SwooleとPHP-FPMの違い
以下は、PHP SwooleとPHP-FPMの違いを比較した表です。
PHP Swoole | PHP-FPM | |
TCP、UDP、HTTP、HTTP2、Unixソケットのサポート | あり | なし。追加のライブラリが必要 |
非同期I/Oの使用 | あり | なし |
各CPUに対してワーカープロセスの分割 – 同時処理のサポート | あり | なし |
PHPファイルをメモリに読み込む | あり | なし |
WebSocketサーバーやTCP/UDPサーバーのための長期接続サポート | あり | なし |
3. Laravel OctaneとPHP-FPMの速度比較
Laravel Octaneの実力を証明するため、簡単なデモを行い、PHP OctaneとPHP-FPMの結果を比較してみましょう。
次のベンチマークテストを仮想マシン(VMware)で行いました:
テストベンチの統計情報:
- CPU: 2コア(11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz)
- RAM: 4GB
- OS: Centos 7
テストアプリケーション:
- Laravel 10.10
- PHP 8.2
ウェブサーバー:
- PHP-FPM
- NGINX
- Laravel Octane
負荷テストツールwrk
を使用して、速度をテストします。
(wrk
は、ウェブサイトの負荷テストを行うために、多数の同時接続をシミュレートするツールです。)
スレッド数(threads)は4、同時接続数(connections)は100、テストにはLaravelで作成したデフォルトのウェルカムページを使用しました。
Laravel + Nginx + PHP-FPM
Laravel Octane + Nginx
結果を見ると、Laravel OctaneはPHP-FPMよりも5倍速く、Laravel Octaneは約42リクエスト/秒を処理できますが、PHP-FPMは約8リクエスト/秒です。
以下は、ブラウザを使用した実際のレスポンスタイムのテスト結果です。
b. ワーカの数の指定
Laravel Octaneアプリケーションを起動すると、ワーカの数はサーバのCPUコア数に対応します(例えば、サーバに2コアがある場合、デフォルトで2つのワーカが作成されます)。これらのワーカは、アプリケーションへのHTTPリクエストを処理するために使用されます。
Octane起動時の進行状況の確認
また、--workers
オプションを使って、ワーカの数を手動で指定することもできます。例えば、以下のように指定します。
php artisan octane:start --workers=4
6ワーカでOctaneを起動したときの進行状況確認
注意点:ワーカの数をサーバのCPUコア数より多く設定すると、リソース競争が発生し、パフォーマンスが低下する可能性があります。したがって、ワーカの数を調整する際は慎重に検討してください。
c. メモリリーク
Octaneは、リクエスト間でアプリケーションをメモリ(RAM)に保持します。そのため、静的オブジェクトにデータを追加すると、メモリリークが発生します。以下の例を見てみましょう:
ルートを routes/web.php
に追加します。
<?php use App\Http\Controllers\TestMemoryLeakerController; use Illuminate\Support\Facades\Route; Route::get('/test', [TestMemoryLeakerController::class, 'test']);
次にファイル App/Services/TestMemoryLeakerService.php
を作成します。
<?php namespace App\Services; class TestMemoryLeakerService { // 静的配列オブジェクト public static array $staticArray = []; // 非静的配列オブジェクト public array $array = []; }
次にファイル App/Http/Controllers/TestMemoryLeakerController.php
を作成します。
<?php namespace App\Http\Controllers; use App\Services\TestMemoryLeakerService; use Illuminate\Support\Str; class TestMemoryLeakerController extends Controller { public function test() { // 1MBの文字列データを作成 TestMemoryLeakerService::$staticArray[] = Str::repeat('a', 1024 * 1024); $memoryUsage = memory_get_peak_usage(true) / 1024 / 1024; // 結果をddで表示し、ページを更新するとメモリが増加し続けるのがわかります dd($memoryUsage . "MB"); } }
上記のコードは、1MBのデータを配列に追加することで、静的オブジェクトにデータを追加し、リクエストごとにメモリが増加することを示しています。これが繰り返されることでメモリが溢れてサーバがクラッシュする原因となります。
この問題を解決するために、依存性注入(Dependency Injection)を使用できます。以下のコードを参照してください:
<?php namespace App\Http\Controllers; use App\Services\TestMemoryLeakerService; use Illuminate\Http\Request; use Illuminate\Support\Str; class TestMemoryLeakerController extends Controller { public function test(TestMemoryLeakerService $testMemoryLeakerService) { // 1MBの文字列データを作成 $testMemoryLeakerService->array[] = Str::repeat('a', 1024 * 1024); $memoryUsage = memory_get_peak_usage(true) / 1024 / 1024; // 結果をddで表示し、ページを更新してもメモリは変わらない dd($memoryUsage . "MB"); } }
b. Octane キャッシュ
Swooleを使用する場合、Octaneのキャッシュドライバを使用することができます。このキャッシュドライバは、1秒あたり最大200万の読み書き操作を提供します。したがって、Octaneのキャッシュドライバは、高速な読み書きが必要なアプリケーションに最適な選択肢です。
キャッシュに保存されたデータはサーバ上のすべてのワーカで共有されます。ただし、サーバを再起動するとキャッシュは削除されます。
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; class TestCacheController extends Controller { public function test() { Cache::store('octane')->put('a', Str::repeat('a', 10), 30); $a = Cache::store('octane')->get('a'); dd($a); } }
注意点:Octaneのキャッシュをクリアするために、php artisan cache:clear
は使用できません。その代わりに、サーバを再起動することでキャッシュをクリアする必要があります。
c. Octane テーブル
Swooleを使用することで、独自のSwooleテーブルを定義し、操作することができます。これらのSwooleテーブルは、非常に高いパフォーマンスでデータを取得することができ、サーバ上のすべてのワーカがこれらのテーブル内のデータにアクセスできます。
ここでデータは、データベースのテーブルのように行と列で保存されます。まず、config/octane.php
ファイルにテーブルを設定する必要があります。以下はその一例です:
<?php 'tables' => [ 'students:1000' => [ 'fullname' => 'string:1000', 'class' => 'string:20', 'created_at' => 'int', ], 'classes:1000' => [ 'class_name' => 'string:1000', 'class_code' => 'string:20', 'created_at' => 'int', ], ],
ここでのstudents:1000
は、そのテーブルに格納できる最大行数を示しています。
各行のstring:n
、1000および20は、列に格納できる最大のバイト数を示しています。
これらの値を増やしたい場合は、config/octane.php
のキャッシュ設定を編集するだけです。
<?php 'cache' => [ 'rows' => 1000, 'bytes' => 10000, ],
以下のようにテーブルを操作することもできます:
<?php namespace App\Http\Controllers; use Carbon\Carbon; use Laravel\Octane\Facades\Octane; class TestTableController extends Controller { public function test() { Octane::table('students')->set('class_monitor', [ 'fullname' => 'Joel Bradley', 'class' => '12A7', 'created_at' => Carbon::now()->timestamp, ]); Octane::table('students')->set('student', [ 'fullname' => 'Reece Bates', 'class' => '12A7', 'created_at' => Carbon::now()->timestamp, ]); $classMonitor = Octane::table('students')->get('class_monitor'); $student = Octane::table('students')->get('student'); dd([ 'class_monitor' => $classMonitor, 'student' => $student, ]); } }
出力:
array:2 [ "class_monitor" => array:3 [ "fullname" => "Joel Bradley" "class" => "12A7" "created_at" => 1706549430 ] "student" => array:3 [ "fullname" => "Reece Bates" "class" => "12A7" "created_at" => 1706549430 ] ]
Octaneテーブルを使用する際の注意点:
- テーブル内のデータは一時的なものであり、サーバ再起動時に失われます。
- Swooleがサポートする列のデータ型は、
string
、int
、float
です。これはメモリ内に保存される一時的なデータ構造であり、データベースの代わりにはなりません。 - テーブルに保存されるデータ量が多いほど、使用するメモリ(RAM)が増えます。
d. Ticks & インターバル
Swooleを使用することで、一定の時間ごとに繰り返し操作を実行するTicks機能を使用できます。これはJavaScriptのsetInterval
メソッドに似たものです。
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Laravel\Octane\Facades\Octane; class AppServiceProvider extends ServiceProvider { /** * アプリケーションサービスのブート処理 */ public function boot(): void { Octane::tick('simple-ticker', fn () => var_dump('Ticking...'))->seconds(2); } }
'simple-ticker'
はこのタスクの名前であり、fn () => var_dump('Ticking...')
は呼び出し可能な関数で、指定された時間ごとに実行されます。
注意:Octaneサーバが動作している間にticksを停止することはできませんので、この機能の使用には注意が必要です。
また、immediate
メソッドを使用すると、Octaneサーバが起動したときに即座にticksが呼び出され、その後指定された秒数ごとに再度ticksが呼ばれます:
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Laravel\Octane\Facades\Octane; class AppServiceProvider extends ServiceProvider { /** * アプリケーションサービスのブート処理 */ public function boot(): void { Octane::tick('simple-ticker', fn () => var_dump('Ticking...'))->seconds(2)->immediate(); } }
7. 結論
Laravel Octaneは、特に並行作業を処理する際にアプリケーションのパフォーマンスを最適化する解決策です。しかし、アプリケーションのライブラリや機能との互換性に注意し、調整が必要です。新しい機能とともに、Laravel OctaneはLaravelアプリケーションの開発において便利で強力なツールとなり、これらの特性を必要とするプロジェクトのパフォーマンスを大幅に向上させることが期待されています。
参照
>>>>> Free Performance + Extra features for Laravel using Octane and Swoole
>>>>> Laravel Octane: With Great Performance Comes Great Responsibility
あなたのウェブサイトのパフォーマンスを向上させるために以下の記事もご覧ください
>>>>> 非同期および遅延JavaScriptを使用したウェブサイトの速度最適化
![]() | Ngụy Minh Tuấn PHP開発者 |