7

我有一个在我的网络服务器上运行的 python 脚本。main 函数被调用,然后当它返回时它只是休眠几秒钟并再次被调用。它的目的是挑选用户添加的任何新上传的视频并将它们转换为 webm,将中间帧拉出作为图像和一堆其他时髦的东西。我正在使用对 ffmpeg 的外部调用。下面的代码片段显示了我如何称呼它。

    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 无限期地挂起。有没有办法可以检查一个进程已经运行了多长时间,如果它已经运行了太长时间,就将其终止?

4

5 回答 5

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)
于 2011-12-28T15:49:33.447 回答
1

Since the controlling script is the one that started it, and since you want it killed based on time, not system resource useage, it should be fairly simple. Below is your example code with some modifications; look for the lines with comments.

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)

You could change the RuntimeError to something else, or have it set a flag instead of raising an exception, depending on what else you need it to do. The child.kill() line will cause the child process to die, but it may not be the most graceful way to end it. If you deploy it on a posix system, you could use os.system('kill -s 15 %i' %child.pid) instead, to kill it more gracefully.

于 2012-01-03T11:29:35.713 回答
0

有一个 python 模块提供了一个接口,用于以可移植的方式检索所有正在运行的进程和系统利用率(CPU、磁盘、内存)的信息,实现了命令行工具提供的许多功能,例如:ps、top、df、kill、免费,lsof,免费,netstat,ifconfig,nice,ionice,iostato,iotop,正常运行时间,tty:psutil。它应该有帮助。

于 2011-12-30T22:06:40.213 回答
-1

看看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
于 2011-12-28T16:02:45.580 回答
-3

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

步骤 2. 不要直接在创建响应的函数中启动子进程。使用芹菜

这个过程一直在服务器上运行。它独立于任何框架,并从 django 填充的同一个数据库中读取

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

于 2011-12-22T12:23:42.813 回答