2025年10月04日
PHP-FPMを使っているアプリケーションで、応答がめっちゃ遅い外部APIをいっぱい叩くとどうなるのか気になったので検証してみる。
予想としては、PHP-FPMのプロセスが枯渇して応答速度が遅くなるはず。
検証方法
- Dockerで以下の環境を作成
- php-fpm8.4
- laravel12
- OPcache有効
- Nginx
- php-fpm8.4
以下のようなLaravel処理を作成
// web.php
// 遅いAPIをシミュレート(10秒待機)
Route::get('/api/slow', function () {
sleep(10);
return response()->json([
'status' => 'success',
'message' => 'This is a slow API response',
]);
});
// 軽量な通常エンドポイント
Route::get('/api/fast', function () {
return response()->json([
'status' => 'success',
'message' => 'This is a fast API response',
]);
});
php−fpmの設定は以下
pm = static
pm.max_children = 3
つまり、PHPの処理ができるプロセス数を3つにしている。
この状態で、wrkを利用し負荷をかける。
負荷をかけた上でブラウザから/api/fast
へctrl + f5でアクセスし、デベロッパーツールでレスポンス時間を測る。
負荷がない状態でのスピード
何も他にアクセスがない状態で/api/fast
へアクセスした場合
以下の画像のように
読み込みに21.47msかかった。
5回確認した結果が以下の通り
- 21.47ms
- 33.74ms
- 22.53ms
- 23.08ms
- 18.74ms
若干のブレはあるけど、30ms以下でだいたい終わるかなくらい。
3つ同時にslowへアクセスがある状態でのスピード
つまり、wrkで
wrk -t3 -c3 -d30s http://localhost:8080/api/slow
のように/slow
へ3つから同時にアクセスし負荷をかける。
この状態で/fast
へブラウザからアクセスするとどうなるか。
以下の画像のように
6秒強かかった。
これは、wrkを実行してから私がアクセスするまでにラグがあるので10秒の遅延にならなかっただけで、予想通りの結果。
5回確認した結果が以下の通り
- 6.13s
- 6.44s
- 3.86s
- 8.06s
- 6.06s
もちろんブレはあるけど、しっかり遅くなっている。
つまり、PHP-FPMを使ってレスポンスの遅いAPIへアクセスする場合は、プロセスの枯渇を考えないといけないということが言える。
もちろん、シンプルに処理が遅い場合も同様。
どう対策するか
これ、結構対策が難しい。
色々考えてみる
プロセス数を増やせばいいのでは?
一番最初に思いつく対策がこれ。 プロセス数を100とか500とかにしておけば問題なさそう。
ただ、これは根本的解決じゃなく、最大接続数とのいたちごっこになるし、プロセスが存在するだけである程度CPUやメモリの固定費を食うはずなので非効率的でもある。
ただ、一時しのぎにはなると思うので、なんかあったらプロセス数を上げたら良さそう。
Laravelのキュー使うとどうなる?
Queueを処理するワーカーにAPI処理任せると…どっちにしろワーカーが詰まるのであんまり意味ないねこれ。
ただ、ワーカーが詰まるだけで本筋のPHP-FPMは詰まらないと思われるので全体の処理が止まるみたいなことは避けられそう。
ポーリングするのはどうか?
実装コストエグいけど
- API通信を送る
- 一旦処理終わらせる
- 数秒に1回終わったかどうか確認する
みたいな感じにすれば解決できなくはなさそう。
ただ、この場合実装コストがえげつないし、相手のAPIサーバーで状態を持ってもらわなきゃいけなくなるのでそもそもこの実装ができない可能性もある。
そのため、現実的ではないかなぁ。
非同期処理サーバーに移行すればいいのでは?
SwooleやRoadRunnerを使い、非同期的に外部APIからデータを取得するようにすれば理論上は恐らくいけるはず。
ただ、実際に検証するのは面倒なのでここではしないし、サーバー変えるって結構な手間なのでよほど覚悟がないと実際にはこの方法で解決はできなさそう。
つまり
つまり、めっちゃ返信が遅いAPIはPHP-FPMで捌くのはやめて、直接そのAPIと通信してもらおう!
以上です。