86 lines
3.8 KiB
Python
86 lines
3.8 KiB
Python
"""One Waitress process owns the background lifecycle and analysis state."""
|
|
import json
|
|
import logging
|
|
import os
|
|
import socket
|
|
import threading
|
|
import time
|
|
from monitor_runtime.paths import data, resource, atomic_json
|
|
from monitor_runtime.bootstrap import prepare_files, initialize_database, has_admin
|
|
from monitor_runtime.licensing import check_license
|
|
|
|
logger = logging.getLogger('monitor.runtime')
|
|
|
|
def run(connection):
|
|
server = None
|
|
manager = None
|
|
ready = False
|
|
try:
|
|
prepare_files()
|
|
initialize_database()
|
|
from monitor_runtime import web
|
|
from django.core.wsgi import get_wsgi_application
|
|
from whitenoise import WhiteNoise
|
|
from waitress import create_server
|
|
from app.utils.GlobalUtils import g_config
|
|
from app.services.lifecycle import get_service_manager
|
|
manager = get_service_manager()
|
|
licensed = check_license()['valid']
|
|
initialized = has_admin()
|
|
port = g_config.adminPort
|
|
app = WhiteNoise(get_wsgi_application(), root=str(resource('public-static')), prefix='static/')
|
|
address = '0.0.0.0' if initialized and licensed else '127.0.0.1'
|
|
server = create_server(app, host=address, port=port, threads=8, channel_timeout=60,
|
|
max_request_body_size=536870912)
|
|
thread = threading.Thread(target=server.run, name='web-server', daemon=True)
|
|
thread.start()
|
|
if licensed and initialized:
|
|
if not manager.start():
|
|
raise RuntimeError('后台服务已被其他进程占用')
|
|
phase = 'ready' if licensed and initialized else 'setup' if not initialized else 'license'
|
|
web.SERVICE_STATE.update(phase=phase, reason=check_license()['reason'])
|
|
url = 'http://127.0.0.1:%d/%s' % (port, 'login' if phase == 'ready' else phase)
|
|
if phase == 'setup':
|
|
url += '#' + web.SETUP_TOKEN
|
|
connection.send({'phase': phase, 'url': url, 'reason': web.SERVICE_STATE['reason']})
|
|
ready = True
|
|
last_check = time.monotonic()
|
|
failures = 0
|
|
while thread.is_alive():
|
|
if connection.poll(1):
|
|
message = connection.recv()
|
|
if message == 'stop':
|
|
return 0
|
|
if web.RESTART_REQUESTED.is_set():
|
|
return 20
|
|
if time.monotonic() - last_check >= 5:
|
|
last_check = time.monotonic()
|
|
status = check_license()
|
|
if not status['valid'] and manager.is_leader:
|
|
web.SERVICE_STATE.update(phase='license', reason=status['reason'])
|
|
connection.send({'phase': 'license', 'url': 'http://127.0.0.1:%d/license' % port,
|
|
'reason': status['reason']})
|
|
# Restart loopback-only for renewal and reliably discard all worker descendants.
|
|
return 20
|
|
if manager.is_leader:
|
|
from app.utils.MediaServerManager import get_media_server_manager
|
|
media = get_media_server_manager()
|
|
failures = 0 if media.managed_pid() and media.api_alive() else failures + 1
|
|
if failures >= 3:
|
|
raise RuntimeError('ZLM 健康检查失败;请查看日志并从托盘重启')
|
|
connection.send({'phase': web.SERVICE_STATE['phase'], 'heartbeat': True})
|
|
raise RuntimeError('Web 服务意外退出')
|
|
except Exception as exc:
|
|
logger.exception('Desktop service failed')
|
|
try:
|
|
connection.send({'phase': 'failed', 'reason': str(exc)})
|
|
except (EOFError, OSError):
|
|
pass
|
|
return 1
|
|
finally:
|
|
if manager:
|
|
manager.stop()
|
|
if server:
|
|
server.close()
|
|
connection.close()
|