perf(runtime): switch socketio to eventlet and optimize asset chunk caching

This commit is contained in:
2026-02-07 16:09:21 +08:00
parent 9d1d4d701e
commit 43f1867033
36 changed files with 228 additions and 124 deletions

74
app.py
View File

@@ -14,6 +14,7 @@ from __future__ import annotations
import atexit
import os
import re
import signal
import sys
import threading
@@ -86,18 +87,38 @@ if not app.config.get("SECRET_KEY"):
cors_origins = os.environ.get("CORS_ALLOWED_ORIGINS", "").strip()
cors_allowed = [o.strip() for o in cors_origins.split(",") if o.strip()] if cors_origins else []
socketio = SocketIO(
app,
cors_allowed_origins=cors_allowed if cors_allowed else None,
async_mode="threading",
ping_timeout=60,
ping_interval=25,
logger=False,
engineio_logger=False,
)
_socketio_preferred_mode = (os.environ.get("SOCKETIO_ASYNC_MODE", "eventlet") or "").strip().lower()
if _socketio_preferred_mode in {"", "auto"}:
_socketio_preferred_mode = None
_socketio_fallback_reason = None
try:
socketio = SocketIO(
app,
cors_allowed_origins=cors_allowed if cors_allowed else None,
async_mode=_socketio_preferred_mode,
ping_timeout=60,
ping_interval=25,
logger=False,
engineio_logger=False,
)
except Exception as socketio_error:
_socketio_fallback_reason = str(socketio_error)
socketio = SocketIO(
app,
cors_allowed_origins=cors_allowed if cors_allowed else None,
async_mode="threading",
ping_timeout=60,
ping_interval=25,
logger=False,
engineio_logger=False,
)
init_logging(log_level=config.LOG_LEVEL, log_file=config.LOG_FILE)
logger = get_logger("app")
if _socketio_fallback_reason:
logger.warning(f"[SocketIO] 初始化失败,已回退 threading 模式: {_socketio_fallback_reason}")
logger.info(f"[SocketIO] 当前 async_mode: {socketio.async_mode}")
init_runtime(socketio=socketio, logger=logger)
_API_DIAGNOSTIC_LOG = str(os.environ.get("API_DIAGNOSTIC_LOG", "0")).strip().lower() in {
@@ -114,6 +135,9 @@ def _is_api_or_health_path(path: str) -> bool:
return raw.startswith("/api/") or raw.startswith("/yuyx/api/") or raw == "/health"
_HASHED_STATIC_ASSET_RE = re.compile(r".*-[a-z0-9_-]{8,}\.(?:js|css|woff2?|ttf|svg|png|jpe?g|webp)$", re.IGNORECASE)
# 初始化安全中间件(需在其他中间件/Blueprint 之前注册)
init_security_middleware(app)
@@ -226,10 +250,15 @@ def serve_static(filename):
if not is_safe_path("static", filename):
return jsonify({"error": "非法路径"}), 403
cache_ttl = 3600
lowered = filename.lower()
if "/assets/" in lowered or lowered.endswith((".js", ".css", ".woff", ".woff2", ".ttf", ".svg")):
is_asset_file = "/assets/" in lowered or lowered.endswith((".js", ".css", ".woff", ".woff2", ".ttf", ".svg"))
is_hashed_asset = bool(_HASHED_STATIC_ASSET_RE.match(lowered))
cache_ttl = 3600
if is_asset_file:
cache_ttl = 604800 # 7天
if is_hashed_asset:
cache_ttl = 31536000 # 365天
if request.args.get("v"):
cache_ttl = max(cache_ttl, 604800)
@@ -247,7 +276,12 @@ def serve_static(filename):
pass
response.headers.setdefault("Vary", "Accept-Encoding")
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}"
if is_hashed_asset:
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}, immutable"
elif is_asset_file:
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}, stale-while-revalidate=60"
else:
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}"
return response
@@ -416,10 +450,12 @@ if __name__ == "__main__":
_init_screenshot_worker_pool()
_warmup_api_connection()
socketio.run(
app,
host=config.SERVER_HOST,
port=config.SERVER_PORT,
debug=config.DEBUG,
allow_unsafe_werkzeug=True,
)
run_kwargs = {
"host": config.SERVER_HOST,
"port": config.SERVER_PORT,
"debug": config.DEBUG,
}
if str(socketio.async_mode) == "threading":
run_kwargs["allow_unsafe_werkzeug"] = True
socketio.run(app, **run_kwargs)