Django, fastcgi: como gerir um processo de longa duração?

votos
8

I ter herdado uma aplicação django + fastcgi que precisa ser modificado para executar um cálculo muito longo (até meia hora ou mais). O que eu quero fazer é executar o cálculo no fundo e retornar uma resposta do tipo seu trabalho foi iniciado. Enquanto o processo está em execução, mais hits para o url deve retornar o seu trabalho ainda está em execução até que o trabalho termina no ponto em que os resultados do trabalho devem ser devolvidos. Qualquer acerto posterior sobre a url deve retornar o resultado em cache.

Eu sou um novato absoluto em Django e não ter feito qualquer trabalho web significativa em uma década, então eu não sei se há uma maneira interna para fazer o que eu quero. Eu tentei iniciar o processo via subprocess.Popen (), e que funciona bem, exceto pelo fato de que deixa uma entrada extinta na tabela de processos. Eu preciso de uma solução limpa que pode remover arquivos temporários e quaisquer vestígios do processo uma vez que tenha terminado.

Eu também experimentei com fork () e tópicos e ainda têm de chegar a uma solução viável. Existe uma solução canônica para o que me parece ser um caso de uso muito comum? FWIW isso só vai ser usado em um servidor interno com tráfego muito baixo.

Publicado 20/10/2008 em 19:05
fonte usuário
Em outras línguas...                            


2 respostas

votos
4

Eu tenho que resolver um problema semelhante agora. Não vai ser um local público, mas da mesma forma, um servidor interno com baixo tráfego.

restrições técnicas:

  • todos os dados de entrada para o processo de longa duração pode ser fornecido em seu início
  • longo processo de execução não exige a interacção do utilizador (excepto para a entrada inicial para iniciar um processo de)
  • o tempo da computação é tempo suficiente para que os resultados não podem ser servido ao cliente em uma resposta HTTP imediato
  • algum tipo de retorno (tipo de barra de progresso) do processo de execução longa é necessária.

Por isso, precisamos de pelo menos duas “visões” web: um para iniciar o processo de longa duração, e o outro, para monitorizar o seu estado / recolher os resultados.

Também precisamos de algum tipo de comunicação entre processos: enviar dados do usuário a partir do iniciador (o servidor web a pedido http) para o processo de longa duração , em seguida, enviar seus resultados para o receptor (de novo servidor web, por base solicitações HTTP). A primeira é fácil, o último é menos óbvia. Ao contrário da programação normal, unix, o receptor não é conhecida inicialmente. O receptor pode ser um processo diferente do iniciador, e pode começar quando o trabalho de longa duração ainda está em andamento ou já está concluído. Assim, os tubos não funcionam e precisamos de algum permamence dos resultados do processo de execução longa.

Eu vejo duas soluções possíveis:

  • lançamentos expedição dos longos processos de execução para o gerente trabalho de longa duração (isto é provavelmente o que o django-fila de serviço acima mencionado é);
  • salvar os resultados de forma permanente, seja em um arquivo ou em DB.

Preferi usar arquivos temporários e para lembrar sua locaiton nos dados da sessão. Eu não acho que ele pode ser mais simples.

Um script de trabalho (este é o processo de longa duração), 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.pymapeamento:

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

views do 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
Respondeu 29/12/2008 em 16:51
fonte usuário

votos
3

Talvez você possa olhar para o problema o contrário.

Talvez você poderia tentar DjangoQueueService , e têm um "daemon" ouvindo a fila, vendo se há algo novo e processá-lo.

Respondeu 20/10/2008 em 19:13
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more