Как проверить состояние или убить внешний процесс с помощью python

У меня есть скрипт на Python, который работает на моем web-сервере. Основная функция вызывается тогда, когда она возвращается, она просто спит в течение нескольких секунд и вызывается снова. Его цель-забрать любые новые загруженные видео, которые пользователи добавили, и преобразовать их в 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 но я, вероятно, разверну его в системе Unix, когда он будет завершен.

проблема. Мне нужен этот скрипт python для продолжения работы. Если что-то пойдет не так с ffmpeg, и мой скрипт зависнет, загруженные пользователем видео будут просто сидеть в состоянии "ожидание", пока я не ткну скрипт python. Я знаю, что определенный файл mov у меня делает его повесят срок. Есть ли способ проверить, как долго процесс работает, а затем убить его, если он работает слишком долго?

5 ответов


Я согласен с С. Лоттом в том, что, казалось бы, вам было бы полезно рассмотреть MQ для вашей архитектуры, но для этого конкретного вопроса я думаю, что вы используете к popen ОК.

для каждого процесса, который вы создаете, сохраните время создания (что-то вроде datetime.datetime.today() хватило бы). Затем каждую минуту или около того просматривайте список открытых процессов и времени и пожинайте те, которые не должны быть там с помощью Popen.send_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)

поскольку управляющий скрипт-это тот, который запустил его, и поскольку вы хотите, чтобы он был убит на основе времени, а не использования системных ресурсов, это должно быть довольно просто. Ниже приведен пример кода с некоторыми изменениями; ищите строки с комментариями.

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 на что-то другое или установить флаг вместо создания исключения, в зависимости от того, что еще вам нужно сделать. Маленький.линия kill() приведет к смерти дочернего процесса, но это возможно, это не самый изящный способ покончить с этим. Если вы развертываете его в системе posix, вы можете использовать ОС.система ('kill-s 15 %i' %child.pid) вместо этого, чтобы убить его более изящно.


посмотри Бог-Монитор Процесса,который контролирует указанный вами процесс и выполняет некоторые действия в соответствии с вашим состоянием мониторинга. Например, он может следить за использованием ЦП и перезапустить процесс, если использование ЦП выше 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

существует модуль python, который предоставляет интерфейс для извлечения информации обо всех запущенных процессах и использовании системы (CPU, диск, память) портативным способом, реализуя множество функций, предлагаемых инструментами командной строки, такими как: ps, top, df, kill, free, lsof, free, netstat, ifconfig, nice, ionice, iostato, iotop, uptime, tty:psutil. Это должно помочь.


Шаг 1. Не используйте сценарии CGI. Используйте фреймворк.

Шаг 2. Не запускайте подпроцесс непосредственно в функции, которая создает ответ. Использовать сельдерей.

этот процесс просто работает на сервере все время. Он не зависит от какой-либо структуры и читает из той же БД, которую заполняет django

Шаг 2 снова. Не оставляйте этот подпроцесс работать все время. Используйте сельдерей, чтобы он начинался, когда запрос поступает, обрабатывает этот запрос (и только этот запрос), а затем останавливается.