私は今、同様の問題を解決する必要があります。公開サイトにはなりませんが、同様に、トラフィックの少ない内部サーバーになります。
技術的な制約:
- 長期実行プロセスへのすべての入力データは、その開始時に提供できます
- 長時間実行されるプロセスはユーザーの操作を必要としません (プロセスを開始するための最初の入力を除く)
- 計算時間が十分に長く、即時の 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