非同期で呼び出したPHPで重い処理をしてもタイムアウトさせない方法

のえる のえる
2021.11.02

画面からサーバーサイドに処理を投げるとき、時間がかかる重たい処理を行わなければならない時があります。

 

たとえば、大量のデータ更新や動画のアップロード、ファイル出力や他システムの連携などがあると思います。

 

そんな時、どうしても発生してしまうのがタイムアウトです。

 

ファイルのアップロードなどですでに時間を使っているのに、利用者はデータ処理にさらに時間を使われて、イライラが止まりません。

 

そんな時、ちょっとした方法で避けることができますので、それをご紹介します。

コマンドで実行する

画面から非同期で実行されるPHPプログラム「A」から、重たいプログラムを実行するPHPプログラム「B」を コマンドライン で実行する方法です。

 

具体的には下記のようなコマンドになります。

 

 

ポイントは > /dev/null & です。

 

これはコマンドラインで非同期にする手段ですが、Linuxでのみ動きます。

 

Windowsでは start ""コマンドの前につけることで非同期にできます。

制限

ただし、この方法にはいくつかの制限があります。

パラメータに上限がある

コマンドラインには長さに制限があります。

 

Windowsには 合計長で8191文字まで という制限があります。

 

LinuxはOSによって異なりますが getconf ARG_MAX で取得できるサイズまでです。

 

つまり、プログラム「B」に渡すパラメータには限りがあるということになります。

 

これはパラメータを外部ファイルにすることで回避が可能ですが、多少手間がかかります。

コマンド プロンプト (Cmd. exe) コマンドライン文字列の制限 / Microsoft

一部のスーパーグローバル変数が利用できない

この方法ではWebサーバーを経由せずにPHPが実行されます。

 

そのため、実行されるプログラム「B」では $_SERVER に入ってくるリファラや Cookie などが使用できません。

 

通情のPHP開発で慣れている人は、多少癖がある書き方になります。

curlで実行する

画面から非同期で実行されるPHPプログラム「A」から、重たいプログラムを実行するPHPプログラム「B」を curl で実行する方法です。

 

 

ポイントは curl_setopt($ch, CURLOPT_TIMEOUT, 1); です。

 

実はこれで実行したプログラムは非同期で動きます。

 

ただし、Webアクセスと同じなので、呼び出される側のプログラム「B」はタイムアウトを0にしておくなどの処理が必要です。

制限

ただし、この方法にもいくつかの制限があります。

一部のスーパーグローバル変数が利用できない

この方法ではWebサーバーを経由せずにPHPが実行されます。

 

そのため、実行されるプログラム「B」では $_SERVER に入ってくるリファラや Cookie などが使用できません。

 

通情のPHP開発で慣れている人は、多少癖がある書き方になります。

セッションを共有できない

これはスーパーグローバル変数に関する内容と重複している部分があります。

 

ログインをセッションで管理しているなどの場合、プログラム「B」では引き継げません。

 

これはCookieをヘッダーに含めることで対応することができます。

 

処理の中断を返して続きをそのまま処理する

これは少し説明が必要ですが、まずはプログラムを見てみましょう。

 

 

このプログラムの後で重たい処理を書けばOKです(ただし、無限ループなどに要注意)。

 

この時点で画面側にはコネクション終了が返され、正常終了となります。

 

しかし、PHPのプログラムはそのあとの部分も実行されるため、ユーザーは終了を検知しつつも、重たいプログラムをそのまま実行することができます。

 

ただ、この時に画面側へ出力を行いたい場合 ob_start などを駆使する必要があります。
(php-fpmの場合 fastcgi_finish_request なども必要になります。)

 

また、処理が確実に終了するように、最後に exit なども忘れずに記述しましょう。

制限

通常のレスポンスが返しづらい

通常は echo などで出力すればOKですが ob_start のあとでしか返せないため、作り方に制限を受けます。

 

また、Webサーバ次第で必要なプログラムが変わることもあり、煩雑になります。

データフローが複数になる

1のプログラムで出力結果が2つ(画面 + プログラム終了)になるため、動きが複雑になりがちです。

 

プログラム次第で多少改善しますが、根本的には変わらないため、仕様書などが書きづらくなる要因でもあります。

プログラムの終了が受け取りづらい

画面側では上記プログラムの時点で終了を受け取っていますが、そのあとの重い処理が完了したかは受け取れません。

 

register_shutdown_function などで無理やり取得することもできるみたいですが、基本的に一方通行となります。

さいごに

いかがでしたでしょうか。

 

通常は同期処理のプログラムによる非同期処理は、そこそこ需要があるのではないでしょうか。

 

実際の実務でも使用できるため、ぜひ試してみてください。

 

ではでは。

スポンサーリンク

POPULAR

のえる

のえる

Full-stack Developer