Я должен решить подобную проблему сейчас. Это будет не публичный сайт, а аналогичный внутренний сервер с низким трафиком.
Технические ограничения:
- все входные данные для длительного процесса могут быть предоставлены при его запуске
- длительный процесс не требует взаимодействия с пользователем (за исключением начального ввода для запуска процесса)
- время вычислений достаточно велико, чтобы результаты не могли быть переданы клиенту в виде немедленного HTTP-ответа
- Требуется какая-то обратная связь (вроде индикатора выполнения) от длительного процесса.
Следовательно, нам нужно по крайней мере два веб-представления: одно для запуска длительного процесса, а другое для контроля его состояния / сбора результатов.
Нам также необходим некоторый тип межпроцессного взаимодействия: отправьте пользовательские данные из инициатора (веб-сервер по запросу http) в длительный процесс , а затем отправьте его результаты Reciever (снова веб-сервер, управляемый http-запросами). Первое легко, последнее менее очевидно. В отличие от обычного программирования Unix, приемник изначально не известен. Получатель может отличаться от инициатора, и он может начаться, когда долгосрочное задание еще выполняется или уже завершено. Таким образом, трубы не работают, и нам нужны некоторые результаты длительного процесса.
Я вижу два возможных решения:
- диспетчеризация запускающих долго процессов в диспетчере долго выполняющихся заданий (вероятно, это и есть вышеупомянутая служба django-queue-service);
- сохранить результаты постоянно, либо в файл или в БД.
Я предпочел использовать временные файлы и запоминать их расположение в данных сеанса. Я не думаю, что это можно сделать проще.
Сценарий задания (это длительный процесс), 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'),
)
django views:
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