refactor: optimize structure, stability and runtime performance
This commit is contained in:
191
app.py
191
app.py
@@ -173,10 +173,28 @@ def serve_static(filename):
|
||||
if not is_safe_path("static", filename):
|
||||
return jsonify({"error": "非法路径"}), 403
|
||||
|
||||
response = send_from_directory("static", filename)
|
||||
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
||||
response.headers["Pragma"] = "no-cache"
|
||||
response.headers["Expires"] = "0"
|
||||
cache_ttl = 3600
|
||||
lowered = filename.lower()
|
||||
if "/assets/" in lowered or lowered.endswith((".js", ".css", ".woff", ".woff2", ".ttf", ".svg")):
|
||||
cache_ttl = 604800 # 7天
|
||||
|
||||
if request.args.get("v"):
|
||||
cache_ttl = max(cache_ttl, 604800)
|
||||
|
||||
response = send_from_directory("static", filename, max_age=cache_ttl, conditional=True)
|
||||
|
||||
# 协商缓存:确保存在 ETag,并基于 If-None-Match/If-Modified-Since 返回 304
|
||||
try:
|
||||
response.add_etag(overwrite=False)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
response.make_conditional(request)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
response.headers.setdefault("Vary", "Accept-Encoding")
|
||||
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}"
|
||||
return response
|
||||
|
||||
|
||||
@@ -232,6 +250,93 @@ def _signal_handler(sig, frame):
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def _cleanup_stale_task_state() -> None:
|
||||
logger.info("清理遗留任务状态...")
|
||||
try:
|
||||
from services.state import safe_get_active_task_ids, safe_remove_task, safe_remove_task_status
|
||||
|
||||
for _, accounts in safe_iter_user_accounts_items():
|
||||
for acc in accounts.values():
|
||||
if not getattr(acc, "is_running", False):
|
||||
continue
|
||||
acc.is_running = False
|
||||
acc.should_stop = False
|
||||
acc.status = "未开始"
|
||||
|
||||
for account_id in list(safe_get_active_task_ids()):
|
||||
safe_remove_task(account_id)
|
||||
safe_remove_task_status(account_id)
|
||||
|
||||
logger.info("[OK] 遗留任务状态已清理")
|
||||
except Exception as e:
|
||||
logger.warning(f"清理遗留任务状态失败: {e}")
|
||||
|
||||
|
||||
def _init_optional_email_service() -> None:
|
||||
try:
|
||||
email_service.init_email_service()
|
||||
logger.info("[OK] 邮件服务已初始化")
|
||||
except Exception as e:
|
||||
logger.warning(f"警告: 邮件服务初始化失败: {e}")
|
||||
|
||||
|
||||
def _load_and_apply_scheduler_limits() -> None:
|
||||
try:
|
||||
system_config = database.get_system_config() or {}
|
||||
max_concurrent_global = int(system_config.get("max_concurrent_global", config.MAX_CONCURRENT_GLOBAL))
|
||||
max_concurrent_per_account = int(system_config.get("max_concurrent_per_account", config.MAX_CONCURRENT_PER_ACCOUNT))
|
||||
get_task_scheduler().update_limits(max_global=max_concurrent_global, max_per_user=max_concurrent_per_account)
|
||||
logger.info(f"[OK] 已加载并发配置: 全局={max_concurrent_global}, 单账号={max_concurrent_per_account}")
|
||||
except Exception as e:
|
||||
logger.warning(f"警告: 加载并发配置失败,使用默认值: {e}")
|
||||
|
||||
|
||||
def _start_background_workers() -> None:
|
||||
logger.info("启动定时任务调度器...")
|
||||
threading.Thread(target=scheduled_task_worker, daemon=True, name="scheduled-task-worker").start()
|
||||
logger.info("[OK] 定时任务调度器已启动")
|
||||
|
||||
logger.info("[OK] 状态推送线程已启动(默认2秒/次)")
|
||||
threading.Thread(target=status_push_worker, daemon=True, name="status-push-worker").start()
|
||||
|
||||
|
||||
def _init_screenshot_worker_pool() -> None:
|
||||
try:
|
||||
pool_size = int((database.get_system_config() or {}).get("max_screenshot_concurrent", 3))
|
||||
except Exception:
|
||||
pool_size = 3
|
||||
|
||||
try:
|
||||
logger.info(f"初始化截图线程池({pool_size}个worker,按需启动执行环境,空闲5分钟后自动释放)...")
|
||||
init_browser_worker_pool(pool_size=pool_size)
|
||||
logger.info("[OK] 截图线程池初始化完成")
|
||||
except Exception as e:
|
||||
logger.warning(f"警告: 截图线程池初始化失败: {e}")
|
||||
|
||||
|
||||
def _warmup_api_connection() -> None:
|
||||
logger.info("预热 API 连接...")
|
||||
try:
|
||||
from api_browser import warmup_api_connection
|
||||
|
||||
threading.Thread(
|
||||
target=warmup_api_connection,
|
||||
kwargs={"log_callback": lambda msg: logger.info(msg)},
|
||||
daemon=True,
|
||||
name="api-warmup",
|
||||
).start()
|
||||
except Exception as e:
|
||||
logger.warning(f"API 预热失败: {e}")
|
||||
|
||||
|
||||
def _log_startup_urls() -> None:
|
||||
logger.info("服务器启动中...")
|
||||
logger.info(f"用户访问地址: http://{config.SERVER_HOST}:{config.SERVER_PORT}")
|
||||
logger.info(f"后台管理地址: http://{config.SERVER_HOST}:{config.SERVER_PORT}/yuyx")
|
||||
logger.info("默认管理员: admin (首次运行随机密码见日志)")
|
||||
logger.info("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
atexit.register(cleanup_on_exit)
|
||||
signal.signal(signal.SIGINT, _signal_handler)
|
||||
@@ -245,81 +350,17 @@ if __name__ == "__main__":
|
||||
init_checkpoint_manager()
|
||||
logger.info("[OK] 任务断点管理器已初始化")
|
||||
|
||||
# 【新增】容器重启时清理遗留的任务状态
|
||||
logger.info("清理遗留任务状态...")
|
||||
try:
|
||||
from services.state import safe_remove_task, safe_get_active_task_ids, safe_remove_task_status
|
||||
# 重置所有账号的运行状态
|
||||
for _, accounts in safe_iter_user_accounts_items():
|
||||
for acc in accounts.values():
|
||||
if getattr(acc, "is_running", False):
|
||||
acc.is_running = False
|
||||
acc.should_stop = False
|
||||
acc.status = "未开始"
|
||||
# 清理活跃任务句柄
|
||||
for account_id in list(safe_get_active_task_ids()):
|
||||
safe_remove_task(account_id)
|
||||
safe_remove_task_status(account_id)
|
||||
logger.info("[OK] 遗留任务状态已清理")
|
||||
except Exception as e:
|
||||
logger.warning(f"清理遗留任务状态失败: {e}")
|
||||
|
||||
try:
|
||||
email_service.init_email_service()
|
||||
logger.info("[OK] 邮件服务已初始化")
|
||||
except Exception as e:
|
||||
logger.warning(f"警告: 邮件服务初始化失败: {e}")
|
||||
_cleanup_stale_task_state()
|
||||
_init_optional_email_service()
|
||||
|
||||
start_cleanup_scheduler()
|
||||
start_kdocs_monitor()
|
||||
|
||||
try:
|
||||
system_config = database.get_system_config() or {}
|
||||
max_concurrent_global = int(system_config.get("max_concurrent_global", config.MAX_CONCURRENT_GLOBAL))
|
||||
max_concurrent_per_account = int(system_config.get("max_concurrent_per_account", config.MAX_CONCURRENT_PER_ACCOUNT))
|
||||
get_task_scheduler().update_limits(max_global=max_concurrent_global, max_per_user=max_concurrent_per_account)
|
||||
logger.info(f"[OK] 已加载并发配置: 全局={max_concurrent_global}, 单账号={max_concurrent_per_account}")
|
||||
except Exception as e:
|
||||
logger.warning(f"警告: 加载并发配置失败,使用默认值: {e}")
|
||||
|
||||
logger.info("启动定时任务调度器...")
|
||||
threading.Thread(target=scheduled_task_worker, daemon=True, name="scheduled-task-worker").start()
|
||||
logger.info("[OK] 定时任务调度器已启动")
|
||||
|
||||
logger.info("[OK] 状态推送线程已启动(默认2秒/次)")
|
||||
threading.Thread(target=status_push_worker, daemon=True, name="status-push-worker").start()
|
||||
|
||||
logger.info("服务器启动中...")
|
||||
logger.info(f"用户访问地址: http://{config.SERVER_HOST}:{config.SERVER_PORT}")
|
||||
logger.info(f"后台管理地址: http://{config.SERVER_HOST}:{config.SERVER_PORT}/yuyx")
|
||||
logger.info("默认管理员: admin (首次运行随机密码见日志)")
|
||||
logger.info("=" * 60)
|
||||
|
||||
try:
|
||||
pool_size = int((database.get_system_config() or {}).get("max_screenshot_concurrent", 3))
|
||||
except Exception:
|
||||
pool_size = 3
|
||||
try:
|
||||
logger.info(f"初始化截图线程池({pool_size}个worker,按需启动执行环境,空闲5分钟后自动释放)...")
|
||||
init_browser_worker_pool(pool_size=pool_size)
|
||||
logger.info("[OK] 截图线程池初始化完成")
|
||||
except Exception as e:
|
||||
logger.warning(f"警告: 截图线程池初始化失败: {e}")
|
||||
|
||||
# 预热 API 连接(后台进行,不阻塞启动)
|
||||
logger.info("预热 API 连接...")
|
||||
try:
|
||||
from api_browser import warmup_api_connection
|
||||
import threading
|
||||
|
||||
threading.Thread(
|
||||
target=warmup_api_connection,
|
||||
kwargs={"log_callback": lambda msg: logger.info(msg)},
|
||||
daemon=True,
|
||||
name="api-warmup",
|
||||
).start()
|
||||
except Exception as e:
|
||||
logger.warning(f"API 预热失败: {e}")
|
||||
_load_and_apply_scheduler_limits()
|
||||
_start_background_workers()
|
||||
_log_startup_urls()
|
||||
_init_screenshot_worker_pool()
|
||||
_warmup_api_connection()
|
||||
|
||||
socketio.run(
|
||||
app,
|
||||
|
||||
Reference in New Issue
Block a user