web-dev-qa-db-ja.com

Pythonでバックグラウンドで長時間実行ジョブを実行する方法

長時間実行されるジョブ(数時間程度)を実行するWebサービスがあります。私はFlask、Gunicorn、nginxを使用してこれを開発しています。

私がやろうとしていることは、完了するまでに長い時間がかかるルートを作成し、スレッドを作成する関数を呼び出すことです。次に、関数はルートにGUIDを返し、ルートは(GUIDを使用して)ユーザーが進捗状況を確認するために使用できるURLを返します。スレッドをデーモンにして(thread.daemon = True)、呼び出しコードが(予期せず)終了するとスレッドが終了するようにします。

これは正しい使用方法ですか?それは機能しますが、それが正しいという意味ではありません。

my_thread = threading.Thread(target=self._run_audit, args=())
my_thread.daemon = True
my_thread.start()
19
Mark

このような問題を処理するためのより一般的な方法は、ベースアプリケーションからアクションを抽出し、 Celery のようなタスクマネージャーシステムを使用して外部で呼び出すことです。

this tutorialを使用すると、タスクを作成してWebアプリケーションからトリガーできます。

from flask import Flask

app = Flask(__name__)
app.config.update(
    CELERY_BROKER_URL='redis://localhost:6379',
    CELERY_RESULT_BACKEND='redis://localhost:6379'
)
celery = make_celery(app)


@celery.task()
def add_together(a, b):
    return a + b

次に、実行できます:

>>> result = add_together.delay(23, 42)
>>> result.wait()
65

ワーカーを個別に実行する必要があることを覚えておいてください:

celery -A your_application worker
8
Ali Nikneshan

セロリとRQは単純なタスクに対してオーバーエンジニアリングしています。このドキュメントをご覧ください- https://docs.python.org/3/library/concurrent.futures.html

Flask app- https://stackoverflow.com/a/39008301/5569578 の例、バックグラウンドで長時間実行ジョブを実行する方法も確認してください

24
Denys Synashko

まあ、あなたのアプローチは不正確ではありませんが、基本的にそれはあなたのプログラムが利用可能なスレッドを使い果たす可能性があります。 ALi で述べたように、一般的なアプローチはRQCeleryのようなジョブキューを使用することです。ただし、これらのライブラリを使用するために関数を抽出する必要はありません。 Flaskの場合、 Flask-RQ を使用することをお勧めします。始めるのは簡単です:

RQ

pip install flask-rq

Flaskアプリで使用する前に、Redisをインストールしてください。

また、Flask関数で@Job Decoratorを使用するだけです。

from flask.ext.rq import job


@job
def process(i):
    #  Long stuff to process


process.delay(3)

そして最後に、ワーカーを開始するためにrqworkerが必要です。

rqworker

詳細は RQ docs を参照してください。単純な長時間実行プロセス用に設計されたRQ。

セロリ

Celeryはより複雑で、機能の膨大なリストがあり、ジョブキューや分散処理方法に慣れていない場合はお勧めしません。

Greenlets

Greenletにはスイッチがあります。長時間実行されているプロセスを切り替えることができます。グリーンレットを使用してプロセスを実行できます。利点は、Redisや他のワーカーが必要ないことです。代わりに、互換性を持つように関数を再設計する必要があります。

from greenlet import greenlet

def test1():
    print 12
    gr2.switch()
    print 34

def test2():
    print 56
    gr1.switch()
    print 78

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
5
Farsheed

あなたのアプローチは問題なく、完全に機能しますが、広く受け入れられているソリューション、つまりセロリが存在するのに、なぜpython Webアプリケーションのバックグラウンドワーカーを再発明するのですか?.

このような重要なタスクのホームロールコードを信頼する前に、多くのテストを行う必要があります。

さらに、セロリは、タスクの永続化や、複数のマシンにワーカーを分散する機能などの機能を提供します。

2
Robert Moskal