ajustes no main_async
This commit is contained in:
parent
c5e5785718
commit
d7f1689610
|
|
@ -1,7 +1,12 @@
|
|||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import signal
|
||||
import traceback
|
||||
import multiprocessing
|
||||
from multiprocessing import Process
|
||||
|
||||
from manager_worker.main import main as iniciar_manager_worker
|
||||
from health_worker.main import main as iniciar_health_worker
|
||||
from visual_worker.main import main as iniciar_visual_worker
|
||||
|
|
@ -9,12 +14,41 @@ from camera_worker.main import main as iniciar_camera_worker
|
|||
from weed_worker.main import main as iniciar_weed_worker
|
||||
from shared.contexto_global_redis import ContextoGlobalRedis
|
||||
|
||||
#sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
def main():
|
||||
ContextoGlobalRedis.reset_operacao()
|
||||
SHUTDOWN = False
|
||||
|
||||
processos = [
|
||||
|
||||
def configurar_console():
|
||||
"""
|
||||
Garante saída UTF-8 quando o processo é iniciado pelo C# com stdout/stderr redirecionados.
|
||||
Evita problemas com emojis/logs em Windows.
|
||||
"""
|
||||
try:
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace", line_buffering=True)
|
||||
|
||||
if hasattr(sys.stderr, "reconfigure"):
|
||||
sys.stderr.reconfigure(encoding="utf-8", errors="replace", line_buffering=True)
|
||||
except Exception:
|
||||
try:
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace", line_buffering=True)
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace", line_buffering=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def log(msg):
|
||||
print(msg, flush=True)
|
||||
|
||||
|
||||
def tratar_sinal(sig, frame):
|
||||
global SHUTDOWN
|
||||
SHUTDOWN = True
|
||||
log(f"⛔ Sinal recebido: {sig}. Encerrando núcleo de workers...")
|
||||
|
||||
|
||||
def criar_processos():
|
||||
return [
|
||||
Process(target=iniciar_manager_worker, name="ManagerWorker"),
|
||||
Process(target=iniciar_health_worker, name="HealthWorker"),
|
||||
Process(target=iniciar_visual_worker, name="VisualWorker"),
|
||||
|
|
@ -22,20 +56,118 @@ def main():
|
|||
Process(target=iniciar_weed_worker, name="WeedWorker"),
|
||||
]
|
||||
|
||||
|
||||
def iniciar_processos(processos):
|
||||
for p in processos:
|
||||
p.daemon = True
|
||||
# Não usar daemon em campo.
|
||||
# Se algum worker precisar criar subprocessos, daemon=True pode quebrar.
|
||||
p.daemon = False
|
||||
p.start()
|
||||
print(f"✅ Processo '{p.name}' iniciado com PID {p.pid}")
|
||||
log(f"✅ Processo '{p.name}' iniciado com PID {p.pid}")
|
||||
|
||||
|
||||
def encerrar_processos(processos, motivo="encerramento solicitado"):
|
||||
log(f"⛔ Encerrando processos filhos: {motivo}")
|
||||
|
||||
for p in processos:
|
||||
try:
|
||||
if p.is_alive():
|
||||
log(f"⛔ Terminando '{p.name}' PID={p.pid}")
|
||||
p.terminate()
|
||||
except Exception as ex:
|
||||
log(f"⚠️ Erro ao terminar '{p.name}': {ex}")
|
||||
|
||||
limite_s = 8
|
||||
inicio = time.time()
|
||||
|
||||
while time.time() - inicio < limite_s:
|
||||
vivos = [p for p in processos if p.is_alive()]
|
||||
if not vivos:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
|
||||
for p in processos:
|
||||
try:
|
||||
if p.is_alive():
|
||||
log(f"🧨 Matando processo resistente '{p.name}' PID={p.pid}")
|
||||
p.kill()
|
||||
except Exception as ex:
|
||||
log(f"⚠️ Erro ao matar '{p.name}': {ex}")
|
||||
|
||||
for p in processos:
|
||||
try:
|
||||
p.join(timeout=2)
|
||||
log(f"✅ Processo '{p.name}' encerrado. ExitCode={p.exitcode}")
|
||||
except Exception as ex:
|
||||
log(f"⚠️ Erro ao aguardar '{p.name}': {ex}")
|
||||
|
||||
|
||||
def monitorar_processos(processos):
|
||||
"""
|
||||
Mantém o processo pai vivo e supervisiona os filhos.
|
||||
Se algum worker morrer sozinho, finaliza todos e sai com erro.
|
||||
O C# deve reiniciar o núcleo.
|
||||
"""
|
||||
ultimo_status = 0
|
||||
|
||||
while not SHUTDOWN:
|
||||
mortos = [p for p in processos if p.exitcode is not None]
|
||||
|
||||
if mortos:
|
||||
for p in mortos:
|
||||
log(f"❌ Worker morreu: {p.name}, PID={p.pid}, ExitCode={p.exitcode}")
|
||||
|
||||
encerrar_processos(processos, motivo="worker morreu")
|
||||
return 2
|
||||
|
||||
agora = time.time()
|
||||
if agora - ultimo_status >= 10:
|
||||
status = ", ".join([f"{p.name}=PID:{p.pid}/alive:{p.is_alive()}" for p in processos])
|
||||
log(f"💓 Supervisor ativo. {status}")
|
||||
ultimo_status = agora
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
encerrar_processos(processos, motivo="shutdown solicitado")
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
configurar_console()
|
||||
|
||||
signal.signal(signal.SIGINT, tratar_sinal)
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("⛔ Encerrando processos...")
|
||||
for p in processos:
|
||||
p.terminate()
|
||||
p.join()
|
||||
print("✅ Todos os processos encerrados.")
|
||||
signal.signal(signal.SIGTERM, tratar_sinal)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
log("🚀 Iniciando supervisor dos workers Python...")
|
||||
log(f"Python exe: {sys.executable}")
|
||||
log(f"CWD: {os.getcwd()}")
|
||||
log(f"Args: {sys.argv}")
|
||||
|
||||
ContextoGlobalRedis.reset_operacao()
|
||||
|
||||
processos = criar_processos()
|
||||
iniciar_processos(processos)
|
||||
|
||||
exit_code = monitorar_processos(processos)
|
||||
|
||||
log(f"✅ Supervisor finalizado. ExitCode={exit_code}")
|
||||
return exit_code
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
multiprocessing.freeze_support()
|
||||
|
||||
try:
|
||||
codigo = main()
|
||||
sys.exit(codigo)
|
||||
except KeyboardInterrupt:
|
||||
log("⛔ KeyboardInterrupt no processo pai.")
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
log("💥 ERRO FATAL NO PROCESSO PAI main_async.py")
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue