2011-12-22 84 views
7

我有一个运行在我的web服务器上的python脚本。主函数被调用,然后当它返回它只是睡几秒钟,再次被调用。目的是获取用户添加的所有新上传的视频并将其转换为webm,将中间框架作为图像和其他一些时髦的东西拉出。我正在使用ffmpeg的外部调用。下面的代码片段显示了我如何称它。如何检查状态或使用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) 

此脚本运行在Windows机器atm上,但我可能会在开发完成时将它部署到Unix系统上。

问题是。我需要这个python脚本来继续运行。如果ffmpeg出现问题并且我的脚本挂起,用户上传的视频就会处于“挂起”状态,直到我去戳python脚本。我知道一个特定的mov文件让ffmpeg不能完全挂起。有没有办法可以检查一个进程已经运行了多长时间,如果运行时间过长,就把它关闭?

+1

我有完全相同的问题(我使用的是Pylons而不是Django)。我编写了一个具有数据库访问权限的外部程序(如果你给它Django的设置,你可以使用你的模型)和ajax轮询系统来获得结果。 – JBernardo 2011-12-29 19:21:36

+0

[芹菜](http://celeryproject.org/)怎么样?有[监测](http://celery.readthedocs.org/en/latest/userguide/monitoring.html)支持。 – danihp 2011-12-30 22:13:44

回答

6

我同意S. Lott的看法,你认为你可以从考虑你的架构的MQ中受益,但对于这个特殊问题,我认为你使用Popen是可以的。

对于您创建的每个过程,保存创建时间(类似datetime.datetime.today()就足够了)。然后每隔一分钟左右浏览打开的进程和时间列表,并使用Popen.send_signal(signal),terminate()或kill()获得不应该在那里的列表。

例子:

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

第1步。不要使用CGI脚本。使用框架。

第2步。不要直接在创建响应的函数中启动子进程。使用celery

此过程只是在服务器上始终运行。它独立于任何框架并且从django填充的同一个分区读取

第2步,再次。不要让这个子进程始终运行。使用Celery,以便在请求到达时启动它,处理该请求(并且仅处理该请求),然后停止。

+0

你在回答中假设了很多。 - 我使用的是django,但这个过程一直在服务器上运行。它独立于任何框架,并从django填充的相同数据库读取。 - 没有响应被创建,当它完成时它只填充了一些数据库字段 – DrLazer 2011-12-22 12:27:32

+0

@DrLazer:我在我的答案中假设了很多,因为你没有在你的问题中提供很多细节。请大家帮忙,并**更新你的问题以获得所有事实。在回答评论时隐瞒事实对其他人来说不是很有帮助。 – 2011-12-22 12:32:01

+1

我提供了足够的细节来涵盖我所问的问题。我很难在一个问题中写出整个系统的规范。 – DrLazer 2011-12-22 12:36:14

0

看看God - A Process Monitor,它监视您指定的进程,并根据您的病情监测执行一些操作。例如,它可以保持上的CPU使用率的眼睛,并且如果CPU使用率是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

有一个Python模块提供了一个接口,用于检索关于所有运行进程和系统信息利用命令行工具提供的许多功能(例如:ps,top,df,kill,free,lsof,free,netstat,ifconfig,nice,ionice,iostato,iotop,正常运行时间,tty:psutil。它应该有所帮助。

1

由于控制脚本是启动它的控件,并且由于您希望它基于时间而不是系统资源使用而死亡,所以它应该非常简单。以下是您的示例代码,并做了一些修改;寻找有评论的线路。

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) 

您可以将RuntimeError更改为其他内容,或者设置标志而不是引发异常,具体取决于您需要执行的操作。 child.kill()行会导致子进程死掉,但它可能不是最优雅的方式来结束它。如果你将它部署在一个posix系统上,你可以使用os.system('kill -s 15%i'%child.pid)来更好地杀死它。