tutorial run docs app aplicacion python django fastcgi

python - run - django, fastcgi: ¿cómo gestionar un proceso de larga ejecución?



docs django project (2)

Heredé una aplicación django + fastcgi que debe modificarse para realizar un cálculo extenso (hasta media hora o más). Lo que quiero hacer es ejecutar el cálculo en segundo plano y devolver una respuesta de tipo "su trabajo ha sido iniciado". Mientras se está ejecutando el proceso, las visitas posteriores a la URL deben devolver "su trabajo aún se está ejecutando" hasta que el trabajo finalice y en ese momento se deben devolver los resultados del trabajo. Cualquier golpe posterior en la url debe devolver el resultado almacenado en caché.

Soy un completo novato en Django y no he hecho ningún trabajo web significativo en una década, así que no sé si hay una forma integrada de hacer lo que quiero. Intenté comenzar el proceso a través del subproceso.Popen (), y eso funciona bien, excepto por el hecho de que deja una entrada difunta en la tabla de proceso. Necesito una solución limpia que pueda eliminar los archivos temporales y cualquier rastro del proceso una vez que haya finalizado.

También experimenté con fork () y con hilos y todavía tengo que encontrar una solución viable. ¿Existe una solución canónica para lo que me parece un caso de uso bastante común? FWIW esto solo se usará en un servidor interno con muy poco tráfico.


Tal vez podrías ver el problema al revés.

Tal vez podrías probar DjangoQueueService , y tener un "daemon" escuchando la cola, ver si hay algo nuevo y procesarlo.


Tengo que resolver un problema similar ahora. No será un sitio público, sino un servidor interno con poco tráfico.

Restricciones técnicas:

  • todos los datos de entrada al proceso de larga duración pueden ser suministrados en su inicio
  • el proceso de larga ejecución no requiere la interacción del usuario (a excepción de la entrada inicial para iniciar un proceso)
  • el tiempo del cálculo es suficientemente largo para que los resultados no se puedan servir al cliente en una respuesta HTTP inmediata
  • se requiere algún tipo de retroalimentación (tipo de barra de progreso) del proceso de larga ejecución.

Por lo tanto, necesitamos al menos dos "vistas" web: una para iniciar el proceso de larga ejecución, y la otra, para monitorear su estado / recopilar los resultados.

También necesitamos algún tipo de comunicación entre procesos: envíe datos de usuario desde el iniciador (el servidor web en la solicitud http) al proceso de larga ejecución , y luego envíe sus resultados al receptor (de nuevo servidor web, impulsado por solicitudes http). El primero es fácil, el último es menos obvio. A diferencia de la programación Unix normal, el receptor no se conoce inicialmente. El receptor puede ser un proceso diferente al del iniciador, y puede comenzar cuando el trabajo de larga ejecución aún está en progreso o ya ha finalizado. Entonces, las tuberías no funcionan y necesitamos una cierta permeabilidad de los resultados del largo proceso.

Veo dos posibles soluciones:

  • despacha los lanzamientos de los procesos de larga ejecución al administrador de trabajos de larga ejecución (esto es probablemente lo que el servicio django-queue-above mencionado anteriormente es);
  • guarde los resultados permanentemente, ya sea en un archivo o en DB.

Preferí usar archivos temporales y recordar su ubicación en los datos de la sesión. No creo que pueda hacerse más simple.

Un script de trabajo (este es el proceso de larga ejecución), 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 mapping:

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

vistas 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