web-dev-qa-db-ja.com

デーモンプロセスとしてPHPスクリプトを実行する

デーモンプロセスとしてphpスクリプトを実行する必要があります(指示を待って作業を行います)。命令が到着するとすぐにアクションを実行する必要があるため、cronジョブはそれを行いません。メモリ管理の問題のため、PHPは実際にはデーモンプロセスに最適なオプションではありませんが、さまざまな理由により、この場合PHPを使用する必要があります。デーモンと呼ばれるlibslackのツールに出会いました( http://libslack.org/daemon )デーモンプロセスの管理に役立つようですが、過去5年間に更新はありませんでした。だから、私の場合に適した他の選択肢を知っているのだろうか。どんな情報でも大歓迎です。

145
Beier

を使用して、コマンドライン(つまりbash)からphpスクリプトを開始できます。

Nohup php myscript.php &

&は、プロセスをバックグラウンドに配置します。

編集:
はい、いくつかの欠点がありますが、制御することはできませんか?それはただ間違っています。
単純なkill processidで停止します。そして、それは依然として最良かつ最も簡単なソリューションです。

159

別のオプションは pstart を使用することです。もともとはUbuntu用に開発されたもので(デフォルトでパッケージ化されています)、すべてのLinuxディストリビューションに適しています。

このアプローチは Supervisord および daemontools に似ており、システムの起動時にデーモンを自動的に起動し、スクリプトの完了時に再起動します。

設定方法:

/etc/init/myphpworker.confに新しいスクリプトファイルを作成します。以下に例を示します。

# Info
description "My PHP Worker"
author      "Jonathan"

# Events
start on startup
stop on shutdown

# Automatically respawn
respawn
respawn limit 20 5

# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
script
    [ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script

デーモンの起動と停止:

Sudo service myphpworker start
Sudo service myphpworker stop

デーモンが実行されているかどうかを確認します。

Sudo service myphpworker status

ありがとう

Kevin van Zonneveld に大いに感謝します。ここでこのテクニックを学びました。

157
Jonathan

新しい systemd を使用すると、サービスを作成できます。

/etc/systemd/system/にファイルまたは symlink を作成する必要があります。 myphpdaemon.serviceとこのようなコンテンツを配置すると、myphpdaemonがサービスの名前になります。

[Unit]
Description=My PHP Daemon Service
#May your script needs MySQL or other services to run, eg. MySQL Memcached
Requires=mysqld.service memcached.service 
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

Restart=on-failure
RestartSec=42s

StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all php output to this one.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target

次のコマンドを使用して、サービスを開始、ステータスの取得、再起動、および停止できます。

systemctl <start|status|restart|stop|enable> myphpdaemon

PHPスクリプトには、実行を継続するための一種の「ループ」が必要です。

<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {

  //Code Logic

  //sleep and usleep could be useful
    if (PHP_SAPI == "cli") {
        if (Rand(5, 100) % 5 == 0) {
            gc_collect_cycles(); //Forces collection of any existing garbage cycles
        }
    }
}

作業例:

[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php  2>&1 > /var/log/app_sync.log'
KillMode=mixed

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

PHPルーチンを(ダイジェストのように)サイクルに1回実行する必要がある場合、直接PHPの代わりに、シェルまたはbashスクリプトを使用してsystemdサービスファイルに呼び出す必要があります。例:

#!/usr/bin/env bash
script_path="/app/services/"

while [ : ]
do
#    clear
    php -f "$script_path"${1}".php" fixedparameter ${2}  > /dev/null 2>/dev/null
    sleep 1
done

これらのオプションを選択した場合、 KillModemixedに変更し、プロセス、bash(main)およびPHP(child)を削除する必要があります。

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

This method also is effective if you're facing a memory leak.

注:「myphpdaemon.service」を変更するたびに、「systemctl daemon-reload」を実行する必要がありますが、変更しないと、必要なときに警告が表示されます。

54
LeonanCarvalho

可能であれば、 NIX環境での高度なプログラミング のコピーを取得します。第13章全体は、デーモンプログラミングに専念しています。例はCですが、必要なすべての関数はPHP(基本的に pcntl および posix 拡張機能)にラッパーを持っています。

いくつかの言葉で-デーモンの作成(これは* nixベースのOS-esでのみ可能です-Windowsはサービスを使用します)は次のようになります。

  1. umask(0) を呼び出して、権限の問題を防ぎます。
  2. fork() で、親を終了します。
  3. setsid() を呼び出します。
  4. SIGHUP(通常これは無視されるか、デーモンに構成を再ロードするように通知するために使用されます)およびSIGTERM(プロセスに正常に終了するよう指示する)の信号処理をセットアップします。
  5. fork()を再度実行し、親を終了します。
  6. chdir() で現在の作業ディレクトリを変更します。
  7. fclose()stdinstdout、およびstderrで、それらに書き込みを行いません。正しい方法は、それらを/dev/nullまたはファイルにリダイレクトすることですが、PHPでそれを行う方法が見つかりませんでした。デーモンを起動してシェルを使用してリダイレクトする場合は可能です(その方法を自分で確認する必要がありますが、わかりません:)。
  8. 仕事をしなさい!

また、PHPを使用しているため、PHP 5.3より前のPHPガベージコレクターにはこれらの参照を収集する方法がなく、プロセスがメモリリークするため、循環参照に注意してください、最終的にクラッシュするまで。

47
Emil Ivanov

多数のPHPデーモンを実行しています。

PHPはこれを行うのに最適な(または優れた)言語ではありませんが、デーモンはWebに面するコンポーネントとコードを共有するため、全体としては良いソリューションです。

これにはdaemontoolsを使用します。スマートで、清潔で、信頼性があります。実際、すべてのデーモンの実行に使用します。

これは http://cr.yp.to/daemontools.html で確認できます。

編集:機能のクイックリスト。

  • 再起動時にデーモンを自動的に起動します
  • 障害時にdameonを自動的に再起動します
  • ロールオーバーとプルーニングを含むロギングが自動的に処理されます
  • 管理インターフェース:「svc」および「svstat」
  • UNIXフレンドリー(多分誰にとってもプラスではない)
24
Phil Wallach

あなたはできる

  1. Henrikが提案したようにNohupを使用します。
  2. screenを使用し、PHPプログラムをその内部で通常のプロセスとして実行します。これにより、Nohupを使用するよりも多くの制御が可能になります。
  3. http://supervisord.org/ のようなデーモンを使用します(Pythonで記述されていますが、コマンドラインプログラムをデーモン化し、それを管理するためのリモートコントロールを提供できます)。
  4. Emilが提案したような独自のデーモン化ラッパーを作成しますが、それはIMOを過剰に実行します。

最も簡単な方法(私の意見では画面)をお勧めします。さらに機能や機能が必要な場合は、より複雑な方法に移行します。

14
Noufal Ibrahim

この問題を解決する方法は複数あります。

詳細はわかりませんが、おそらくPHPプロセスをトリガーする別の方法があります。たとえば、SQLデータベースのイベントに基づいてコードを実行する必要がある場合は、スクリプトを実行するトリガーを設定できます。これはPostgreSQLで簡単に実行できます: http://www.postgresql.org/docs/current/static/external-pl.html .

正直なところ、Nohupを使用してDamonプロセスを作成するのが最善の策だと思います。 Nohupを使用すると、ユーザーがログアウトした後でもコマンドの実行を継続できます。

Nohup php myscript.php &

ただし、非常に深刻な問題があります。 PHPのメモリマネージャーは完全なゴミであると言ったように、スクリプトは数秒間しか実行されずに存在するという前提で構築されました。 PHPスクリプトは、数日後にGIGABYTESのメモリを使用し始めます。また、12時間ごとまたは24時間ごとに実行されるcronスクリプトを作成して、次のようにphpスクリプトを強制終了および再生成する必要があります。

killall -3 php
Nohup php myscript.php &

しかし、スクリプトが仕事の途中にあった場合はどうでしょうか? Kill -3は割り込みです。CLIでctrl + cを実行するのと同じです。 phpスクリプトは、PHP pcntlライブラリを使用してこの割り込みをキャッチし、正常に終了できます。 http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

以下に例を示します。

function clean_up() {
  GLOBAL $lock;
  mysql_close();
  fclose($lock)
  exit();
}
pcntl_signal(SIGINT, 'clean_up');

$ lockの背後にある考え方は、PHPスクリプトがfopen( "file"、 "w");でファイルを開くことができるということです。 1つのプロセスのみがファイルの書き込みロックを保持できるため、これを使用すると、PHPスクリプトのコピーが1つだけ実行されていることを確認できます。

幸運を!

11
rook

Kevin van Zonneveld これに関する非常に詳細な記事を書いた 、彼の例では System_Daemon PEAR package (最後のリリース2009-09-02の日付)。

10
Alix Axel

チェックアウト https://github.com/shaneharter/PHP-Daemon

これは、オブジェクト指向のデーモンライブラリです。ロギングやエラー回復などのサポートが組み込まれており、バックグラウンドワーカーの作成もサポートされています。

6
Shane H

最近、PHPスクリプトをデーモンとして実行する問題に対するクロスプラットフォームソリューション(Windows、Mac、Linux)が必要になりました。独自のC++ベースのソリューションを作成し、バイナリを作成することで問題を解決しました。

https://github.com/cubiclesoft/service-manager/

Linux(sysvinit経由)を完全にサポートしますが、Windows NTサービスとMac OSXも起動しました。

Linuxだけが必要な場合は、ここで紹介する他のいくつかのソリューションが十分に機能し、フレーバーにもよります。最近はUpstartとsystemdもあり、sysvinitスクリプトへのフォールバックがあります。しかし、PHPを使用するポイントの半分は、本質的にクロスプラットフォームであるということです。そのため、この言語で書かれたコードは、どこでもそのまま動作する可能性が非常に高くなります。特定の外部ネイティブOSレベルの側面がシステムサービスなどの状況に入ると、欠陥が現れ始めますが、ほとんどのスクリプト言語ではこの問題が発生します。

ここで誰かがPHPユーザーランドで提案したようにシグナルをキャッチしようとするのは良い考えではありません。 pcntl_signal()のドキュメントを注意深く読むと、PHPがプロセスではめったに見られない何かのサイクルを噛むかなり不快なメソッド(具体的には 'ticks')を使用してシグナルを処理することがすぐにわかります。 (つまり、シグナル)。 PHPでのシグナル処理もPOSIXプラットフォームでかろうじてしか利用できず、サポートはPHPのバージョンによって異なります。最初はまともな解決策のように聞こえますが、真に有用であるとは言えません。

PHPは、時間の経過とともにメモリリークの問題も改善しています。まだ注意する必要があります(DOM XMLパーサーはまだリークする傾向があります)が、最近では暴走プロセスはほとんど見られず、PHPバグトラッカーは昔の日と比べてかなり静かです。

3
CubicleSoft

他の人がすでに述べたように、PHPをデーモンとして実行するのは非常に簡単で、1行のコマンドを使用して実行できます。しかし、実際の問題は、それを実行し、管理することです。私はかなり前に同じ問題を抱えており、すでに利用可能なソリューションはたくさんありますが、それらの多くは多くの依存関係を持っているか、使用が難しく、基本的な使用には適していません。 PHP cliスクリプトを含む任意のプロセス/アプリケーションを管理できるシェルスクリプトを作成しました。アプリケーションを起動するcronjobとして設定でき、アプリケーションを含めて管理します。同じcronjobなどを介して再度実行された場合、アプリが実行されているかどうかを確認し、実行されている場合は単に終了し、前のインスタンスがアプリケーションの管理を続行できるようにします。

Githubにアップロードしましたので、お気軽に使用してください: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

アプリケーションを監視するだけです(起動、再起動、ログ、監視など)。アプリケーションが適切に実行されていることを確認する汎用スクリプト。意図的にpid/lockファイルのプロセス名instreadを使用して、すべての副作用を防ぎ、スクリプトを可能な限りシンプルで攪拌し続けるようにします。そのため、EasyDaemonizer自体が再起動されても常に動作します。特徴

  • アプリケーションを起動し、オプションで起動ごとにカスタマイズされた遅延を開始します
  • 1つのインスタンスのみが実行されていることを確認します
  • CPU使用率を監視し、定義されたしきい値に達するとアプリを自動的に再起動します
  • EasyDeamonizerをcron経由で実行するように設定し、何らかの理由で停止した場合に再度実行する
  • アクティビティを記録します
1
Sina Salek

拡張Emil Ivaov answer、phpでSTDIN、STDOUTおよびSTDERRORを閉じるために次のことができます

if (!fclose(STDIN)) {
    exit("Could not close STDIN");
}

if (!fclose(STDOUT)) {
    exit("Could not close STDOUT");
}

if (!fclose(STDERR)) {
    exit("Could not close STDERR");
}

$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('/dev/null', 'w');
$STDERR = fopen('/var/log/our_error.log', 'wb');

基本的に、標準ストリームを閉じて、PHPに書き込む場所がないようにします。次のfopen呼び出しは、標準のIOを/dev/nullに設定します。

これをRob Aley-PHPウェブの向こう側の本から読みました。

1
Raheel

シンプルなphp-daemonを作成してデプロイしました。コードはここでオンラインです

https://github.com/jmullee/PhpUnixDaemon

機能:特権のドロップ、シグナル処理、ロギング

キューハンドラーで使用しました(ユースケース:ページ生成phpを待機させずに、Webページから長時間の操作をトリガーします。つまり、非同期操作を起動します) https://github.com/jmullee/ PhpIPCMessageQueue

0
jmullee

ここでpm2を確認できます http://pm2.keymetrics.io/

対処するphpスクリプトにworker.shなどのsshファイルを作成します。

worker.sh

php /path/myscript.php

デーモン開始

pm2 start worker.sh

乾杯、それだけです。

0
Serkan Koch