2017-01-03 17 views
2

誰かが特定のビューを訪れたときに、サブプロセスで長期実行バックグラウンドプロセスを開始する必要があります。ゾンビプロセスを作成せずにフラスコのバックグラウンドプロセスを停止する

私のコード

from flask import Flask 
import subprocess 

app = Flask(__name__) 

@app.route("/") 
def index(): 
    subprocess.Popen(["sleep", "10"]) 
    return "hi\n" 

if __name__ == "__main__": 
    app.run(debug=True) 

これは、ほとんどの部分は、素晴らしい作品。

問題は、プロセス(スリープ)が終了すると、ps -Af | grep sleep[sleep] <defunct>と表示されます。

私が読んだところでは、これはまだプロセスの参照がflaskにあるためです。

プロセスの終了後にこの参照を削除する方法はありますか?

私はので、私はそれにdelを使用することができますが、これはサブプロセスが終了するまで応答を返すからフラスコを防ぐg.subprocess = subprocess.Popen(["sleep", "10"])をやって、そしてプロセスは@app.after_request(response)で終了するのを待ってみました - 私はそれがサブプロセスが終了する前に応答を返す必要があります。

私はノンブロッキングであることをsubprocess.Popen操作を必要とする - これは重要です。

+0

' Thread'クラスを呼び出してそこにコードを実装してください。そして、それを閉じるには、 'join'を使用してください[https://docs.python.org/2/library/threading.html#threading.Thread.join](https:// docs.python.org/2/library/threading.html#threading.Thread.join) – Valijon

+0

Celery(http://www.celeryproject.org/)などの堅牢な方法を使用するようにプロジェクトを修正してください。代わりにバックグラウンド処理を行います。 – jsbueno

答えて

1

subprocess.Popenを使って「貧弱な人の並列処理」をたくさん見て、それを自由に動かすことができましたが、それはしばしばあなたが指摘したようにゾンビの問題につながります。

あなたは(あなたは、プロセスが失敗した場合に例外を発生する場合、その場合には、 Popenの必要は、ちょうど callまたは check_callを使用しない)スレッドで、あなたのプロセスを実行することができ

callまたはcheck_call(またはrun、Python 3.5以降)は、プロセスが完了するのを待ってゾンビがなく、スレッドで実行しているのでブロックされていません。

import threading 

def in_background(): 
    subprocess.call(["sleep", "10"]) 

@app.route("/") 
def index(): 
    t = threading.Thread(target=in_background) 
    t.start() 
    return "hi\n" 

注:t.join()を使用し、そのためにあなたがtスレッドオブジェクトの参照を保持する必要があると思いますがあるだろうスレッドの完了を待ちます。

ところで、私は(いつものコースのスレッドで!)あなたの本当プロセスがsleepではない、またはそれは非常に有用ではないですし、time.sleep(10)が同じことをしたと

+0

最終的なアプリはnginxとgunicornによって実行され、フラスコアプリの何十ものプロセスを持つことができます。 subprocess.Popenによって起動されるプロセスは、それらすべてにデータを提供します。私はもともとスレッドを使用しようとしましたが、スレッドを起動したページを開いた元のユーザーがページから離れてしまうと、スレッドは終了し、他のすべてのプロセス(フラスコアプリケーションのインスタンス)はサービスを停止します。バックグラウンドプロセスは、バックグラウンドスレッドよりもうまくいくようです。 – John

+0

あなたはすでにスレッドで 'subprocess.Popen'を使用したことを意味していますか?またはスレッドだけ? –

+0

これは実演中で、何十ものリクエストがあり、Celeryを使って私がコメントに示唆したように、確かに行く方法です。 – jsbueno

2

私はコメントで提案してきたようにPythonでこのようなことを達成する最もクリーンで最も堅牢な方法の1つは、celeryを使用することです。

セロリーでは、rabbitmqがデフォルトのメッセージング用のブローカートランスポートと、少なくともワーカーを実行しているプロセスが必要です。しかし、可読性と意志を高めることは、ワーカーコードがサーバーアプリケーションと同じファイルに共存できるということです。リモートプロシージャは、単純な関数呼び出しの場所と同じように呼び出されます。

セロリは、再試行、タスク後のイベント、およびその他の多くのことを無償で処理できます。長年にわたって実用化された成熟したコードのすべてが処理されます。

これは、セロリで使用するためにそれを再足す後のあなたの例である:RabbitMQのサーバはデフォルトのオプションで実行しているシステムでは、このコードでは

from flask import Flask 
from celery import Celery 
import subprocess 

app = Flask(__name__) 
celery_app = Celery("test") 

@celery_app.task 
def run_process(): 
    subprocess.Popen(["sleep", "5"]) 

@app.route("/") 
def index(): 
    run_process.delay() 
    return "hi\n" 

if __name__ == "__main__": 
    app.run(debug=True, port=8080) 

(私はパッケージをインストールし、サービスを開始しました何も設定はありません。もちろん、実際には調整する必要がありますが、すべてが同じサーバ上にある場合には、それは必要ではないかもしれません)。

rabbitmqを使用すると、 celery worker -A bla1.celery_app -Dのようなコマンドライン(あなたがあなたのFlaskを持っている同じvirtualenvのpipインストールセロリー)。次に、フラスコサーバーを起動して、動作していることを確認します。

もちろん、外部プロセスを呼び出すだけでなく、Python自体でもっと多くの作業をしている場合、これはさらに利点があります。あなたのデータベースモデルにアクセスすることができ、その中のオブジェクトを変更するアシンクロナスアクションを実行することができます(ユーザーセッションの「フラッシュ」メッセージや電子メールなど)。

関連する問題