web-dev-qa-db-ja.com

Djangoスレッド/処理で長時間実行される非同期タスク

免責事項:私はSOにいくつかの同様の質問があることを知っています。私はそれらのすべてではないにしても、ほとんど読んだと思いますが、私の実際の質問に対する回答は見つかりませんでした(後述)。また、セロリまたはその他の非同期キューシステムを使用することは、長時間実行するタスクを達成するための最良の方法であること、または少なくともcron管理のスクリプトを使用することを知っています。 プロセスとスレッドに関するmod_wsgi doc もありますが、すべてが正しいかどうかはわかりません。

質問は:

そこにリストされているソリューションの使用に関連する正確なリスク/問題は何ですか?それらのいずれかは、長時間実行されるタスクに適していますか(セロリの方が適していますが)。私の質問は、全体的な最善のソリューションを見つけることよりも、wsgiとpython/Djangoの内部を理解することについてです。スレッドのブロック、変数への安全でないアクセス、ゾンビ処理などの問題.

まあ言ってみれば:

  1. 私の「long_process」は本当に安全なことをしています。それが失敗したとしても、私は気にしません。
  2. python> = 2.6
  3. デーモンモードでApacheでmod_wsgiを使用しています(uwsgiまたはgunicornで何か変更されますか?)

mod_wsgi conf:

WSGIDaemonProcess NAME user=www-data group=www-data threads=25
WSGIScriptAlias / /path/to/wsgi.py
WSGIProcessGroup %{ENV:VHOST}

私はこれらが個別のプロセス(広い意味で)を起動してユーザーに迅速に応答を返しながら長時間実行タスクを実行するために利用できるオプションであることを理解しました:

os.fork

import os

if os.fork()==0:
    long_process()
else:
    return HttpResponse()

サブプロセス

import subprocess

p = subprocess.Popen([sys.executable, '/path/to/script.py'], 
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

(スクリプトがmanage.pyコマンドである可能性が高い場合)

スレッド

import threading

t = threading.Thread(target=long_process,
                             args=args,
                             kwargs=kwargs)
t.setDaemon(True)
t.start()
return HttpResponse()

NB。

グローバルインタープリターロックにより、CPythonではPythonコードを一度に実行できるのは1つのスレッドのみです(特定のパフォーマンス指向のライブラリーがこの制限を克服できる場合でも)。アプリケーションでマルチコアマシンの計算リソースを使用する場合は、マルチプロセッシングを使用することをお勧めしますが、複数のI/Oバウンドタスクを同時に実行する場合は、スレッド化が適切なモデルです。

メインスレッドはすぐに戻ります(httpresponse)。生成された長いスレッドは、wsgiが別のリクエストに対して他のことを行うのをブロックしますか?

マルチプロセッシング

from multiprocessing import Process

p = Process(target=_bulk_action,args=(action,objs))
p.start()
return HttpResponse()

これにより、スレッドの同時実行性の問題が解決されますか?


だから私は考えられる選択肢だ。何が機能し、何が機能しないのか、そしてその理由は?

30
Stefano

os.fork

フォークは親プロセスのクローンを作成します。この場合は、Djangoスタックです。別のpythonスクリプトを実行したいだけなので、この不必要な量の膨らみのようです。

サブプロセス

subprocessの使用はインタラクティブであることが期待されています。言い換えれば、これを使用してプロセスを効果的に生成することができますが、ある時点で、終了時にプロセスを終了することが期待されます。 Pythonを実行したままにしておくとクリーンアップされる可能性がありますが、実際にはメモリリークが発生すると思われます。

スレッド

スレッドは、定義されたロジックの単位です。 run()メソッドが呼び出されると開始し、run()メソッドの実行が終了すると終了します。これにより、現在のスコープ外で実行されるロジックのブランチを作成するのに適しています。しかし、あなたが述べたように、それらはグローバル通訳者ロックの対象です。

マルチプロセッシング

これは基本的にステロイドのスレッドです。スレッドの利点がありますが、グローバルインタープリターロックの対象ではなく、マルチコアアーキテクチャを利用できます。ただし、その結果、操作が複雑になります。

ですから、あなたの選択は本当にスレッドかマルチプロセッシングに帰着します。スレッドを使用して問題が解決され、アプリケーションにとって意味がある場合は、スレッドを使用してください。それ以外の場合は、マルチプロセッシングを使用します。

27
Chris Pratt

WSGI Decorators を使用すると、バックグラウンドで長いタスクを実行する必要がある場合に、Celeryを使用するよりもquiteの方が簡単であることを発見しました。セロリは深刻な重いプロジェクトに最適なソリューションであり、単純なことをするためのオーバーヘッドだと思います。

WSGI Decorators の使用を開始するには、uWSGI構成を次のように更新するだけです

<spooler-processes>1</spooler-processes>
<spooler>/here/the/path/to/dir</spooler>

次のようなコードを書きます:

@spoolraw
def long_task(arguments):
    try:
        doing something with arguments['myarg'])
    except Exception as e:
        ...something...
    return uwsgi.SPOOL_OK

def myView(request)
    long_task.spool({'myarg': str(someVar)})
    return render_to_response('done.html')

UWSGIログでビューを開始すると、次のように表示されます。

[spooler] written 208 bytes to file /here/the/path/to/dir/uwsgi_spoolfile_on_hostname_31139_2_0_1359694428_441414

そしてタスクが終了したとき:

[spooler /here/the/path/to/dir pid: 31138] done with task uwsgi_spoolfile_on_hostname_31139_2_0_1359694428_441414 after 78 seconds

奇妙な(私にとって)制限があります:

    - spool can receive as argument only dictionary of strings, look like because it's serialize in file as strings.
    - spool should be created on start up so "spooled" code it should be contained in separate file which should be defined in uWSGI config as <import>pyFileWithSpooledCode</import>
11
Oleg Neumyvakin

質問について:

生成された長いスレッドは、wsgiが別のリクエストに対して他のことを行うのをブロックしますか?

答えはいいえだ。

リクエストからバックグラウンドスレッドを作成する場合は注意が必要です。ただし、膨大な数のバックグラウンドスレッドを作成して、プロセス全体を詰まらせてしまう場合があります。処理中のものを実行している場合でも、タスクキューシステムが本当に必要です。

Webプロセス、特にApacheからforkまたはexecを実行することに関しては、Apacheは作成されたサブプロセスの環境に、その操作に技術的に干渉する可能性のある奇妙な条件を課す可能性があるため、一般に良い考えではありません。

Celeryのようなシステムを使用することは、おそらく最善の解決策です。

3