我继承了一个django + fastcgi应用程序,需要对其进行修改以执行冗长的计算(最多半小时或更长时间)。我想要做的是在后台运行计算并返回“你的工作已经开始” - 类型的响应。当进程正在运行时,对URL的进一步命中应返回“您的作业仍在运行”,直到作业完成,此时应返回作业的结果。对url的任何后续命中都应该返回缓存的结果。
我是django的一个新手,并且在十年内没有做过任何重要的网络工作,所以我不知道是否有内置的方法来做我想要的。我已经尝试通过subprocess.Popen()启动该过程,并且它工作正常,除了它在进程表中留下一个已解除的条目。我需要一个干净的解决方案,可以在完成后删除临时文件和进程的任何痕迹。
我还尝试过fork()和线程,但尚未提出可行的解决方案。对于我来说似乎是一个非常常见的用例,是否有规范的解决方案? FWIW只会在流量非常低的内部服务器上使用。
答案 0 :(得分:4)
我现在必须解决类似的问题。它不会是一个公共站点,但同样也是一个流量较低的内部服务器。
技术限制:
因此,我们至少需要两个网络“视图”:一个用于启动长时间运行的进程,另一个用于监视其状态/收集结果。
我们还需要某种进程间通信:将用户数据从发起程序(http请求中的Web服务器)发送到长时间运行的进程,然后发送它结果发送到接收方(再次是由http请求驱动的Web服务器)。前者很容易,后者则不太明显。与普通的unix编程不同,接收器最初并不为人所知。接收器可以是与发起者不同的过程,并且它可以在长时间运行的作业仍在进行或已经完成时开始。所以管道不起作用,我们需要对长时间运行过程的结果进行一些永久性的处理。
我看到两种可能的解决方案:
我更喜欢使用临时文件并记住会话数据中的位置。我不认为它可以变得更简单。
作业脚本(这是一个长时间运行的过程),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()
django urls.py
映射:
urlpatterns = patterns('',
(r'^startjob/$', 'mysite.myapp.views.startjob'),
(r'^showjob/$', 'mysite.myapp.views.showjob'),
(r'^rmjob/$', 'mysite.myapp.views.rmjob'),
)
django观点:
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
答案 1 :(得分:3)
也许你可以反过来看问题。
也许你可以尝试DjangoQueueService,并让一个“守护进程”监听队列,看看是否有新东西并处理它。