diff --git a/app.py b/app.py index f21edd17..ba361a09 100755 --- a/app.py +++ b/app.py @@ -1,28 +1,44 @@ -# ============ Gevent Monkey Patching(必须放在所有 import 之前!)============ -# 用于支持 Gunicorn + gevent 异步模式,使 requests 等阻塞调用变为非阻塞 -# 注意:如果使用 GeventWebSocketWorker,worker 会自动 patch,这里检测避免重复 +# ============ Eventlet/Gevent Monkey Patching(必须放在所有 import 之前!)============ +# 用于支持 Gunicorn + eventlet/gevent 异步模式,使 requests 等阻塞调用变为非阻塞 import os import sys -def _is_already_patched(): - """检测是否已经被 gevent patch""" +def _detect_async_env(): + """检测当前异步环境""" + # 检测 eventlet try: - from gevent import monkey - return monkey.is_module_patched('socket') - except: - return False - -# 只在未被 patch 且明确启用时才 patch -if os.environ.get('GEVENT_SUPPORT', 'false').lower() == 'true' and not _is_already_patched(): - try: - from gevent import monkey - monkey.patch_all(thread=False, subprocess=False) # 避免 patch 某些可能有问题的模块 - print("✅ Gevent monkey patching 已启用 (手动)") + import eventlet + if hasattr(eventlet, 'is_monkey_patched') and eventlet.is_monkey_patched('socket'): + return 'eventlet_patched' + return 'eventlet_available' except ImportError: - print("⚠️ Gevent 未安装,跳过 monkey patching") -elif _is_already_patched(): + pass + + # 检测 gevent + try: + from gevent import monkey + if monkey.is_module_patched('socket'): + return 'gevent_patched' + return 'gevent_available' + except ImportError: + pass + + return 'none' + +_async_env = _detect_async_env() + +# Gunicorn eventlet worker 会自动 patch,这里只打印状态 +if _async_env == 'eventlet_patched': + print("✅ Eventlet monkey patching 已由 Worker 启用") +elif _async_env == 'gevent_patched': print("✅ Gevent monkey patching 已由 Worker 启用") -# ============ Gevent Monkey Patching 结束 ============ +elif _async_env == 'eventlet_available': + print("📡 Eventlet 可用,等待 Gunicorn worker 初始化") +elif _async_env == 'gevent_available': + print("📡 Gevent 可用,等待 Gunicorn worker 初始化") +else: + print("⚠️ 未检测到 eventlet 或 gevent,将使用 threading 模式") +# ============ Monkey Patching 检测结束 ============ import base64 import csv diff --git a/gunicorn_app_config.py b/gunicorn_app_config.py index a5b00e55..ba1c0f01 100644 --- a/gunicorn_app_config.py +++ b/gunicorn_app_config.py @@ -23,13 +23,15 @@ import os bind = '0.0.0.0:5001' # Worker 进程数 -# 对于 16 核心机器,使用 4-8 个 gevent workers 即可(每个可处理数千并发) -# 建议: min(8, CPU_CORES / 2) 因为 gevent 本身是异步的,不需要太多进程 -workers = 4 # 4 个 gevent workers,每个可处理 2000 并发连接 = 8000 总并发 +# 重要: Flask-SocketIO + eventlet 使用多 worker 时需要 Redis 消息队列 +# 单 worker + eventlet 可以处理数千并发连接(异步 I/O) +# 多 worker 需要设置环境变量: SOCKETIO_USE_QUEUE=true +workers = 1 # 默认单 worker,启用消息队列后可改为 4 -# Worker 类型 - 使用 gevent(新版 Flask-SocketIO 不需要 geventwebsocket) +# Worker 类型 - 使用 eventlet(完整支持 WebSocket) # 参考: https://flask-socketio.readthedocs.io/en/latest/deployment.html -worker_class = 'gevent' +# 注意: eventlet 模式下建议使用单 worker,多 worker 需要 Redis 消息队列 +worker_class = 'eventlet' # Worker 连接数(gevent 异步模式下可以处理大量并发连接) worker_connections = 2000 diff --git a/requirements.txt b/requirements.txt index 24d85a1f..d0bd010f 100755 --- a/requirements.txt +++ b/requirements.txt @@ -19,6 +19,7 @@ celery==5.3.4 PyMySQL==1.1.0 gevent==23.9.1 gevent-websocket==0.10.1 +eventlet==0.33.3 psutil==5.9.6 Pillow==10.1.0 itsdangerous==2.1.2