#python #django #subprocess #uwsgi
#python #django #подпроцесс #uwsgi
Вопрос:
У меня есть некоторый существующий код Django, работающий под uwsgi (под Linux) с отключенной обработкой потоков, который выполняет для некоторых запросов подпроцесс, который я не могу контролировать.
Обычная операция выполняется следующим образом:
- подпроцесс выполняется в течение довольно короткого времени и возвращает либо код выхода 0, либо что-то другое. Код запишет некоторые сообщения в stdout / stderr. Код возврата (код выхода) сообщит мне, была ли работа выполнена правильно. если выполнение завершилось неудачно, было бы неплохо собрать stdout / stderr и зарегистрировать его, чтобы понять, почему что-то не удалось.
Однако в редких случаях подпроцесс может столкнуться с пока не понятым состоянием гонки и выполнит следующее.
- он будет повторно записывать определенное сообщение в stdout и stderr и зацикливаться и зависать навсегда.
Поскольку я не знаю, существуют ли какие-либо другие условия гонки, которые могут заморозить процесс с выводом или без него. Id’ также хотел бы добавить тайм-аут. (Хотя решение, которое адресует получение кода возврата и обнаружение повторного сообщения, уже было бы хорошим достижением.
То, что я пробовал до сих пор, это:
import os
import select
import subprocess
import time
CMD = ["bash", "-c", "echo hello"]
def run_proc(cmd=CMD, timeout=10):
""" run a subprocess, fetch (and analyze stdout / stderr) and
detect if script runs too long
and exit when script finished
"""
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = proc.stdout.fileno()
stderr = proc.stderr.fileno()
t0 = time.time()
while True:
if time.time() - t0 > timeout:
print("TIMEOUT")
break
rc = proc.returncode
print("RC", rc)
if proc.returncode is not None:
break
to_rd, to_wr, to_x = select.select([stdout, stderr], [], [], 2)
print(to_rd, to_wr, to_x)
if to_rd:
if stdout in to_rd:
rdata = os.read(stdout, 100)
print("S:", repr(rdata))
if stderr in to_rd:
edata = os.read(stderr, 100)
print("E:", repr(edata))
print(proc.returncode)
На самом деле мне не нужно, чтобы stdout и stderr обрабатывались отдельно, но это ничего не изменило
Однако, когда подпроцесс завершил вывод, происходит что-то действительно странное.
the output of select tells me, that stdout and stderr can be read from, but when I read I get an empty string.
proc.returncode is still None
Как я мог исправить приведенный выше код или как я мог бы сохранить свою проблему другим способом?
Ответ №1:
Проверьте хотя бы Popen.poll():
def run_proc(cmd=CMD, timeout=10):
""" run a subprocess, fetch (and analyze stdout / stderr) and
detect if script runs too long
and exit when script finished
"""
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = proc.stdout.fileno()
stderr = proc.stderr.fileno()
t0 = time.time()
while True:
returncode = proc.poll()
print("RC", returnode)
if returncode is not None:
break
if time.time() - t0 > timeout:
print("TIMEOUT")
# You need to kill the subprocess, break doesn't stop it!
proc.terminate()
# wait for the killed process to 'reap' the zombie
proc.wait()
break
to_rd, to_wr, to_x = select.select([stdout, stderr], [], [], 2)
print(to_rd, to_wr, to_x)
if to_rd:
if stdout in to_rd:
rdata = os.read(stdout, 100)
print("S:", repr(rdata))
if stderr in to_rd:
edata = os.read(stderr, 100)
print("E:", repr(edata))
print(returncode)
Выход:
RC None
[3] [] []
S: b'hellon'
RC None
[3, 5] [] []
S: b''
E: b''
0
Комментарии:
1. большое спасибо.
proc.poll()
мне помогает. Однакоwhile proc.poll() is None and proc.returncode is None:
я думаю, что его можно было бы изменить наwhile proc.poll():
или, возможно, наwhile True:
следующийrc = proc.poll()
, и где-тоif rc is not None: break
2. еще одна проблема.
proc.terminate()
кажется, оставляет зомби. Любой способ избежать этого. поскольку uwsgi работает очень долго, существует теоретический риск оставить после себя довольно много зомби3.
proc.terminate()
послеproc.wait()
этого, похоже, устраняется проблема с зомби4. Я повысил ваш ответ. Не могли бы вы, пожалуйста, адаптировать свой ответ так, чтобы
proc.terminate()
proc.wait()
вызывался и, и, возможно, изменить оператор while, чтобы было понятнее, что результатомproc.poll()
являетсяproc.returncode
или, если вы предпочитаете, я мог бы добавить эти изменения в ваш ответ, а затем пометить этот ответ как решение5. @gelonida: Отредактируйте мой ответ, пожалуйста.