2011-12-22 5 views
7

Ho uno script python che viene eseguito sul mio server web. La funzione principale viene chiamata quindi quando viene restituita, rimane in attesa per alcuni secondi e viene richiamata di nuovo. Lo scopo è raccogliere nuovi video caricati che gli utenti hanno aggiunto e convertirli in webm, estrarre il frame centrale come un'immagine e un mucchio di altre cose funky. Sto usando una chiamata esterna a ffmpeg. Il codice clip qui sotto mostra come lo chiamo.Come si controlla lo stato o si elimina un processo esterno 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) 

Questo script è in esecuzione su una macchina Windows atm ma probabilmente distribuire su un sistema Unix quando è dev completa.

Il problema è. Ho bisogno di questo script python per continuare a funzionare. Se qualcosa va storto con ffmpeg e il mio script si blocca, i video caricati dall'utente rimarranno in uno stato "in sospeso" fino a quando non vado a colpire lo script python. So che un certo file mov che ho rende ffmpeg sospeso indefinitamente. È in qualche modo possibile verificare per quanto tempo è stato eseguito un processo e poi eliminarlo se è stato eseguito troppo a lungo?

+1

Ho avuto esattamente lo stesso problema (stavo usando piloni invece di Django). Ho scritto un programma esterno con accesso al database (se le dai impostazioni Django, puoi usare i tuoi modelli) e un sistema di polling ajax per ottenere il risultato. – JBernardo

+0

Che dire di [sedano] (http://celeryproject.org/). Ha il supporto [monitoring] (http://celery.readthedocs.org/en/latest/userguide/monitoring.html). – danihp

risposta

6

Sono d'accordo con S. Lott in quanto sembrerebbe che tu possa trarre vantaggio dal considerare un MQ per la tua architettura, ma per questo particolare problema penso che il tuo uso di Popen sia OK.

Per ogni processo che si crea, salvare il tempo di creazione (qualcosa come datetime.datetime.today() sarebbe sufficiente). Poi, ogni minuto, passa alla lista dei processi e dei tempi aperti e raccogli quelli che non dovrebbero essere lì usando Popen.send_signal (signal), terminate() o kill().

Esempio:

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

Passaggio 1. Non utilizzare script CGI. Usa un quadro.

Passaggio 2. Non avviare il sottoprocesso direttamente nella funzione che crea la risposta. Utilizzare celery.

questo processo è in esecuzione sul server tutto il tempo. È indipendente da qualsiasi framework e legge dallo stesso db che popola django

Passaggio 2, di nuovo. Non lasciare questo sottoprocesso sempre in esecuzione. Usa Celery in modo che venga avviato quando arriva una richiesta, gestisce quella richiesta (e solo quella richiesta) e poi si ferma.

+0

Hai assunto molto nella tua risposta. - Sto usando django, ma questo processo è in esecuzione sul server tutto il tempo. È indipendente da qualsiasi quadro e legge dallo stesso db che popola il django. - Nessuna risposta viene creata, riempie semplicemente alcuni campi DB quando viene eseguito – DrLazer

+0

@DrLazer: Sto assumendo molto nella mia risposta perché non hai fornito molti dettagli nella tua domanda. Faccia tutti un favore e ** aggiorna ** la tua domanda per avere tutti i fatti. Nascondere i fatti in un commento a una risposta non è di grande aiuto per gli altri. –

+1

Fornisco abbastanza dettagli per coprire la domanda che sto chiedendo. Difficilmente scriverò l'intera specifica per il sistema in una domanda. – DrLazer

0

Date un'occhiata a God - A Process Monitor, che monitora il processo specificato, ed eseguire alcune azioni in base alle proprie condizioni di monitoraggio. Ad esempio, si può tenere d'occhio l'utilizzo della CPU e riavviare il processo se l'utilizzo della CPU è superiore al 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

C'è un modulo Python che fornisce un'interfaccia per il recupero di informazioni su tutti i processi in esecuzione e il sistema utilizzo (CPU, disco, memoria) in modo portatile, implementando molte funzionalità offerte da strumenti a linea di comando quali: ps, top, df, kill, free, lsof, free, netstat, ifconfig, nice, ionice, iostato, iotop, uptime, tty: psutil. Dovrebbe aiutare

1

Poiché lo script di controllo è quello che l'ha avviato, e dal momento che si desidera che venga ucciso in base al tempo, non all'utilizzo delle risorse di sistema, dovrebbe essere piuttosto semplice. Di seguito è riportato il codice di esempio con alcune modifiche; cercare le linee con i commenti.

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) 

Si potrebbe cambiare il RuntimeError a qualcos'altro, o lo hanno impostato una bandiera invece di sollevare un'eccezione, a seconda di che cosa avete bisogno di fare. La linea child.kill() causerà la morte del processo figlio, ma potrebbe non essere il modo più elegante per terminarlo. Se lo si distribuisce su un sistema posix, si può usare os.system ('kill -s 15% i'% child.pid), per ucciderlo più facilmente.