如何使用 python 检查状态或终止外部进程

发布于 2024-12-22 17:12:45 字数 1114 浏览 1 评论 0原文

我有一个在我的网络服务器上运行的 python 脚本。主函数被调用,然后当它返回时,它只是休眠几秒钟,然后再次被调用。它的目的是拾取用户添加的任何新上传的视频并将其转换为 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 无限期挂起。有什么方法可以检查进程运行了多长时间,如果运行时间太长,则将其杀死吗?

I have a python script that runs on my web server. The main function is called then when it returns it just sleeps for a few seconds and gets called again. It's purpose is to pick up any new uploaded videos that users have added and convert them to webm, pull out the middle frame as an image and a bunch of other funky stuff. I am using an external call to ffmpeg. The code clip below shows how I call it.

    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)

This script is running on a Windows machine atm but I will probably deploy it on a Unix system when it is dev complete.

The problem is. I need this python script to keep running. If something goes wrong with ffmpeg and my script hangs, user uploaded videos will just sit in a "pending" status until I go poke the python script. I know a certain mov file I have makes ffmpeg hang indefinately. Is there someway I can check how long a process has been running and then kill it off if it has been running for too long?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小忆控 2024-12-29 17:12:45

我同意 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)

I agree with S. Lott in that it would seem you'd benefit from considering a MQ for your architecture, but for this particular issue I think your use of Popen is OK.

For each process you create, save the creating time (something like datetime.datetime.today() would suffice). Then every minute or so go over the list of open processes and times and reap the ones that shouldn't be there using Popen.send_signal(signal), terminate(), or kill().

Example:

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)
天赋异禀 2024-12-29 17:12:45

由于控制脚本是启动它的脚本,并且您希望根据时间而不是系统资源使用情况来终止它,因此它应该相当简单。以下是经过一些修改的示例代码;查找带有注释的行。

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) 来更优雅地杀死它。

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.

带刺的爱情 2024-12-29 17:12:45

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

There is a python module that provides an interface for retrieving information on all running processes and system utilization (CPU, disk, memory) in a portable way, implementing many functionalities offered by command line tools such as: ps, top, df, kill, free, lsof, free, netstat, ifconfig, nice, ionice, iostato, iotop, uptime, tty: psutil. It should help.

温柔嚣张 2024-12-29 17:12:45

看看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

Take a look at God - A Process Monitor,which monitors the process you specified, and perform some actions according to your monitoring condition. For example, it can keep an eye on the cpu usage and restart the process if the cpu usage is above 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
你与昨日 2024-12-29 17:12:45

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

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

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

再次从 django 填充步骤 2 的同一数据库读取。不要让这个子进程一直运行。使用 Celery,以便它在请求到达时启动,处理该请求(并且仅处理该请求),然后停止。

Step 1. Don't use CGI scripts. Use a framework.

Step 2. Don't start the subprocess directly in the function which creates the response. Use celery.

this process is just running on the server all the time. It's independent of any framework and reads from the same db that django populates

Step 2, again. Don't leave this subprocess running all the time. Use Celery so that it is started when a request arrives, handles that request (and only that request) and then stops.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文