8

長い計算 (最大 30 分以上) を実行するために変更する必要がある django+fastcgi アプリケーションを継承しました。私がやりたいことは、計算をバックグラウンドで実行し、「ジョブが開始されました」タイプの応答を返すことです。プロセスの実行中に、ジョブの結果が返される時点でジョブが終了するまで、URL へのさらなるヒットは「your job is still running」を返す必要があります。URL の後続のヒットは、キャッシュされた結果を返す必要があります。

私は django のまったくの初心者であり、10 年間重要な Web 作業を行っていないため、自分がやりたいことを行うための組み込みの方法があるかどうかわかりません。subprocess.Popen() を介してプロセスを開始しようとしましたが、プロセス テーブルに無効なエントリが残るという事実を除いて、正常に動作します。プロセスが終了したら、一時ファイルとプロセスの痕跡を削除できるクリーンなソリューションが必要です。

私は fork() とスレッドも試しましたが、実行可能な解決策をまだ見つけていません。私にはかなり一般的なユースケースと思われるものに対する標準的な解決策はありますか? FWIW これは、トラフィックが非常に少ない内部サーバーでのみ使用されます。

4

2 に答える 2

4

私は今、同様の問題を解決する必要があります。公開サイトにはなりませんが、同様に、トラフィックの少ない内部サーバーになります。

技術的な制約:

  • 長期実行プロセスへのすべての入力データは、その開始時に提供できます
  • 長時間実行されるプロセスはユーザーの操作を必要としません (プロセスを開始するための最初の入力を除く)
  • 計算時間が十分に長く、即時の HTTP 応答で結果をクライアントに提供できない
  • 長時間実行されるプロセスからの何らかのフィードバック (進行状況バーのようなもの) が必要です。

したがって、少なくとも 2 つの Web「ビュー」が必要です。

また、ある種のプロセス間通信も必要です。イニシエーター(http 要求の Web サーバー) から長時間実行されるプロセスにユーザー データを送信し、その結果を受信側(ここでも HTTP 要求によって駆動される Web サーバー) に送信します。前者は簡単ですが、後者はそれほど明白ではありません。通常の UNIX プログラミングとは異なり、受信側は最初はわかりません。レシーバーはイニシエーターとは異なるプロセスである可能性があり、長時間実行されるジョブがまだ進行中または既に終了しているときに開始される可能性があります。そのため、パイプは機能せず、長時間実行されるプロセスの結果を永続化する必要があります。

考えられる解決策は 2 つあります。

  • 長時間実行されるプロセスの起動を長時間実行されるジョブ マネージャーにディスパッチします (これはおそらく、上記の django-queue-service と同じです)。
  • 結果をファイルまたは DB に永続的に保存します。

私は一時ファイルを使用し、セッション データ内の場所を記憶することを好みました。もっとシンプルにできないと思います。

ジョブ スクリプト (これは長時間実行されるプロセスです) myjob.py:

import sys
from time import sleep

i = 0
while i < 1000:
    print 'myjob:', i  
    i=i+1
    sleep(0.1)
    sys.stdout.flush()

ジャンゴのurls.pyマッピング:

urlpatterns = patterns('',
(r'^startjob/$', 'mysite.myapp.views.startjob'),
(r'^showjob/$',  'mysite.myapp.views.showjob'),
(r'^rmjob/$',    'mysite.myapp.views.rmjob'),
)

ジャンゴビュー:

from tempfile import mkstemp
from os import fdopen,unlink,kill
from subprocess import Popen
import signal

def startjob(request):
     """Start a new long running process unless already started."""
     if not request.session.has_key('job'):
          # create a temporary file to save the resuls
          outfd,outname=mkstemp()
          request.session['jobfile']=outname
          outfile=fdopen(outfd,'a+')
          proc=Popen("python myjob.py",shell=True,stdout=outfile)
          # remember pid to terminate the job later
          request.session['job']=proc.pid
     return HttpResponse('A <a href="/showjob/">new job</a> has started.')

def showjob(request):
     """Show the last result of the running job."""
     if not request.session.has_key('job'):
          return HttpResponse('Not running a job.'+\
               '<a href="/startjob/">Start a new one?</a>')
     else:
          filename=request.session['jobfile']
          results=open(filename)
          lines=results.readlines()
          try:
               return HttpResponse(lines[-1]+\
                         '<p><a href="/rmjob/">Terminate?</a>')
          except:
               return HttpResponse('No results yet.'+\
                         '<p><a href="/rmjob/">Terminate?</a>')
     return response

def rmjob(request):
     """Terminate the runining job."""
     if request.session.has_key('job'):
          job=request.session['job']
          filename=request.session['jobfile']
          try:
               kill(job,signal.SIGKILL) # unix only
               unlink(filename)
          except OSError, e:
               pass # probably the job has finished already
          del request.session['job']
          del request.session['jobfile']
     return HttpResponseRedirect('/startjob/') # start a new one
于 2008-12-29T15:51:23.920 に答える
3

たぶん、問題を逆に見ることができます。

たぶん、 DjangoQueueServiceを試して、「デーモン」がキューをリッスンし、何か新しいものがあるかどうかを確認して処理することができます。

于 2008-10-20T18:13:38.067 に答える