
PHPやJavaでPythonを実行する2つの方法

画面からサーバーサイドに処理を投げるとき、時間がかかる重たい処理を行わなければならない時があります。
たとえば、大量のデータ更新や動画のアップロード、ファイル出力や他システムの連携などがあると思います。
そんな時、どうしても発生してしまうのがタイムアウトです。
ファイルのアップロードなどですでに時間を使っているのに、利用者はデータ処理にさらに時間を使われて、イライラが止まりません。
そんな時、ちょっとした方法で避けることができますので、それをご紹介します。
目次
画面から非同期で実行されるPHPプログラム「A」から、重たいプログラムを実行するPHPプログラム「B」を コマンドライン で実行する方法です。
具体的には下記のようなコマンドになります。
1 2 3 |
exec('php /path/to/B.php > /dev/null &'); |
ポイントは > /dev/null & です。
これはコマンドラインで非同期にする手段ですが、Linuxでのみ動きます。
Windowsでは start "" をコマンドの前につけることで非同期にできます。
ただし、この方法にはいくつかの制限があります。
コマンドラインには長さに制限があります。
Windowsには 合計長で8191文字まで という制限があります。
LinuxはOSによって異なりますが getconf ARG_MAX で取得できるサイズまでです。
つまり、プログラム「B」に渡すパラメータには限りがあるということになります。
これはパラメータを外部ファイルにすることで回避が可能ですが、多少手間がかかります。
この方法ではWebサーバーを経由せずにPHPが実行されます。
そのため、実行されるプログラム「B」では $_SERVER に入ってくるリファラや Cookie などが使用できません。
通情のPHP開発で慣れている人は、多少癖がある書き方になります。
画面から非同期で実行されるPHPプログラム「A」から、重たいプログラムを実行するPHPプログラム「B」を curl で実行する方法です。
1 2 3 4 5 6 7 8 |
$url = "プログラム「B」までのURL"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_TIMEOUT, 1); curl_exec($ch); curl_close($ch); |
ポイントは curl_setopt($ch, CURLOPT_TIMEOUT, 1); です。
実はこれで実行したプログラムは非同期で動きます。
ただし、Webアクセスと同じなので、呼び出される側のプログラム「B」はタイムアウトを0にしておくなどの処理が必要です。
ただし、この方法にもいくつかの制限があります。
この方法ではWebサーバーを経由せずにPHPが実行されます。
そのため、実行されるプログラム「B」では $_SERVER に入ってくるリファラや Cookie などが使用できません。
通情のPHP開発で慣れている人は、多少癖がある書き方になります。
これはスーパーグローバル変数に関する内容と重複している部分があります。
ログインをセッションで管理しているなどの場合、プログラム「B」では引き継げません。
これはCookieをヘッダーに含めることで対応することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Cookie情報を取得して、CURLOPT_HTTPHEADER形式へ変換 $cookie = ''; if(is_array($_COOKIE) && !empty($_COOKIE)) { $cookieAry = []; foreach($_COOKIE as $key => $val) { $cookieAry[] = $key.'='.$val.';'; } $cookie = implode(' ', $cookieAry); } $header = [ 'Cookie: '.$cookie, ]; // CURLのオプションでヘッダーを設定 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); |
これは少し説明が必要ですが、まずはプログラムを見てみましょう。
1 2 3 4 5 6 |
ignore_user_abort(true); set_time_limit(0); header('Connection: close'); header('Content-Length: 0'); |
このプログラムの後で重たい処理を書けばOKです(ただし、無限ループなどに要注意)。
この時点で画面側にはコネクション終了が返され、正常終了となります。
しかし、PHPのプログラムはそのあとの部分も実行されるため、ユーザーは終了を検知しつつも、重たいプログラムをそのまま実行することができます。
ただ、この時に画面側へ出力を行いたい場合
ob_start などを駆使する必要があります。
(php-fpmの場合
fastcgi_finish_request なども必要になります。)
また、処理が確実に終了するように、最後に exit なども忘れずに記述しましょう。
通常は echo などで出力すればOKですが ob_start のあとでしか返せないため、作り方に制限を受けます。
また、Webサーバ次第で必要なプログラムが変わることもあり、煩雑になります。
1のプログラムで出力結果が2つ(画面 + プログラム終了)になるため、動きが複雑になりがちです。
プログラム次第で多少改善しますが、根本的には変わらないため、仕様書などが書きづらくなる要因でもあります。
画面側では上記プログラムの時点で終了を受け取っていますが、そのあとの重い処理が完了したかは受け取れません。
register_shutdown_function などで無理やり取得することもできるみたいですが、基本的に一方通行となります。
いかがでしたでしょうか。
通常は同期処理のプログラムによる非同期処理は、そこそこ需要があるのではないでしょうか。
実際の実務でも使用できるため、ぜひ試してみてください。
ではでは。
POPULAR
のえる
Full-stack Developer
人気記事