Как проверить состояние или убить внешний процесс с помощью 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 снова. Не оставляйте этот подпроцесс работать все время. Используйте сельдерей, чтобы он начинался, когда запрос поступает, обрабатывает этот запрос (и только этот запрос), а затем останавливается.