2011-12-22 10 views
7

Tengo un script de python que se ejecuta en mi servidor web. La función principal se invoca y cuando vuelve solo duerme durante unos segundos y se vuelve a llamar. Su propósito es recoger los nuevos videos subidos que los usuarios agregaron y convertirlos a webm, sacar el fotograma medio como una imagen y un montón de otras cosas funky. Estoy usando una llamada externa a ffmpeg. El siguiente clip de código muestra cómo lo llamo.Cómo verifica el estado o cancela un proceso externo con python

duration = output[durationIndex+10:durationIndex+18] 
    durationBits = duration.split(":") 
    lengthInSeconds = (int(durationBits[0])*60*60) + (int(durationBits[1])*60) + (int(durationBits[2])) 

    child = subprocess.Popen(["ffmpeg","-y","-i",sourceVideo,"-f","mjpeg","-vframes","1","-ss",str(lengthInSeconds/2),destination], shell=True, stderr=subprocess.PIPE) 
    output = "" 
    while True: 
     out = child.stderr.read(1) 
     if out == '' and child.poll() != None: 
      break 
     if out != '': 
      output += out 

    updateSQL = "update `videos_graduatevideo` set thumbnail = '" + str(destination) + "' where `original_video` = '" + sourceVideo + "'" 
    cursor.execute(updateSQL) 

Este script se ejecuta en una máquina de Windows atm pero probablemente va a desplegar en un sistema Unix cuando es dev completa.

El problema es. Necesito este script python para seguir funcionando. Si algo sale mal con ffmpeg y se cuelga mi script, los videos subidos por el usuario simplemente se colocarán en un estado "pendiente" hasta que yo pise el script de python. Sé que cierto archivo mov que tengo hace ffmpeg colgar indefinidamente. ¿De alguna manera puedo verificar cuánto tiempo ha estado funcionando un proceso y luego matarlo si ha estado funcionando por mucho tiempo?

+1

Tuve exactamente el mismo problema (estaba usando Pilones en lugar de Django). Escribí un programa externo con acceso a la base de datos (si le das la configuración de Django, puedes usar tus modelos) y un sistema de votación ajax para obtener el resultado. – JBernardo

+0

¿Qué pasa con [apio] (http://celeryproject.org/)? Tiene soporte [de monitoreo] (http://celery.readthedocs.org/en/latest/userguide/monitoring.html). – danihp

Respuesta

6

Estoy de acuerdo con S. Lott en que parece que sería beneficioso considerar un MQ para su arquitectura, pero para este problema en particular creo que su uso de Popen está bien.

Para cada proceso que cree, guarde la hora de creación (algo como datetime.datetime.today() sería suficiente). Luego, cada minuto más o menos revisa la lista de procesos y tiempos abiertos y cosecha los que no deberían estar allí usando Popen.send_signal (signal), terminate() o kill().

Ejemplo:

import time 
from subprocess import Popen 
from datetime import datetime 
jobs = [] 
max_life = 600 # in seconds 

def reap_jobs(jobs): 
    now = datetime.datetime.today() 
    for job in jobs: 
    if job[0] < now - datetime.timedelta(seconds=max_life) 
     job[1].kill() 
     # remove the job from the list if you want. 
     # but remember not to do it while iterating over the list 

for video in list_of_videos: 
    time = datetime.datetime.today() 
    job = Popen(...) 
    jobs.append((time,child)) 

while True: 
    reap_jobs(jobs) 
    time.sleep(60) 
-3

Paso 1. No use scripts CGI. Usa un marco.

Paso 2. No inicie el subproceso directamente en la función que crea la respuesta. Use celery.

este proceso se está ejecutando en el servidor todo el tiempo. Es independiente de cualquier marco y lee desde el mismo archivo db que django rellena

Paso 2, nuevamente. No deje este subproceso ejecutándose todo el tiempo. Use Apio para que se inicie cuando llegue una solicitud, maneje esa solicitud (y solo esa solicitud) y luego se detenga.

+0

Has asumido mucho en tu respuesta. - Estoy usando django, pero este proceso solo se ejecuta en el servidor todo el tiempo. Es independiente de cualquier marco y lee desde el mismo archivo db que django rellena. - No se genera ninguna respuesta, simplemente rellena algunos campos de base de datos cuando se hace – DrLazer

+0

@DrLazer: Asumo mucho en mi respuesta porque no proporcionó muchos detalles en su pregunta. Haznos un favor a todos nosotros y ** actualiza ** tu pregunta para tener todos los hechos. Ocultar hechos en un comentario a una respuesta no es terriblemente útil para otros. –

+1

Proporciono detalles suficientes para cubrir la pregunta que estoy formulando. Apenas voy a escribir todas las especificaciones para el sistema en una pregunta. – DrLazer

0

Tome un vistazo a God - A Process Monitor, que supervisa el proceso que ha especificado, y realizar algunas acciones de acuerdo a su condición de monitoreo. Por ejemplo, se puede mantener un ojo en el uso de la CPU y reiniciar el proceso si el uso de CPU está por encima de 50%:

# code in Ruby 
# copyied from the documentation 
w.restart_if do |restart| 
    restart.condition(:cpu_usage) do |c| 
    c.above = 50.percent 
    c.times = 5 
    end 
end 
0

Hay un módulo de Python que proporciona una interfaz para la recuperación de información en todos los procesos en ejecución y sistema de utilización (CPU, disco, memoria) de forma portátil, implementando muchas funcionalidades ofrecidas por herramientas de línea de comandos tales como: ps, top, df, kill, free, lsof, libre, netstat, ifconfig, nice, ionice, iostato, iotop, tiempo de actividad, tty: psutil. Debería ayudar.

1

Dado que la secuencia de comandos de control es la que la inició, y dado que desea eliminarla en función del tiempo, no del uso de recursos del sistema, debería ser bastante simple. A continuación se muestra su código de ejemplo con algunas modificaciones; busca las líneas con comentarios.

import time 
timeout = 60 #child is allowed to run for 1 minute. 
duration = output[durationIndex+10:durationIndex+18] 
durationBits = duration.split(":") 
lengthInSeconds = (int(durationBits[0])*60*60) + (int(durationBits[1])*60) + (int(durationBits[2])) 

child = subprocess.Popen(["ffmpeg","-y","-i",sourceVideo,"-f","mjpeg","-vframes","1","-ss",str(lengthInSeconds/2),destination], shell=True, stderr=subprocess.PIPE) 
killtime = time.time() + timeout #timestamp after which the child process should be killed 
output = "" 
while True: 
    out = child.stderr.read(1) 
    if out == '' and child.poll() != None: 
     break 
    if out != '': 
     output += out 
    if time.time() > killtime: #check if 60 seconds have passed 
     child.kill() #tell the child to exit 
     raise RuntimeError("Child process still going %i seconds after launch" %killtime) #raise an exception so that updateSQL doesn't get executed 

updateSQL = "update `videos_graduatevideo` set thumbnail = '" + str(destination) + "' where `original_video` = '" + sourceVideo + "'" 
cursor.execute(updateSQL) 

podría cambiar el RuntimeError a otra cosa, o hacer que se establece un indicador en lugar de lanzar una excepción, dependiendo de qué más se necesita hacer. La línea child.kill() hará que el proceso hijo muera, pero puede no ser la forma más elegante de finalizarlo. Si lo despliega en un sistema posix, podría usar os.system ('kill -s 15% i'% child.pid) en su lugar, para matarlo con más gracia.

Cuestiones relacionadas