152 lines
4.8 KiB
Python
152 lines
4.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Gunicorn 配置文件 - app.py 生产环境配置(支持 Flask-SocketIO + WebSocket + 多进程)
|
||
|
||
使用方式:
|
||
# 推荐方式: 使用此配置文件启动(多 Worker 模式)
|
||
gunicorn -c gunicorn_app_config.py app:app
|
||
|
||
# 单 Worker 调试模式:
|
||
gunicorn -c gunicorn_app_config.py -w 1 app:app
|
||
|
||
多进程架构说明:
|
||
- Flask Session 使用 Redis 存储(db=1),所有 Worker 共享
|
||
- SocketIO 使用 Redis 消息队列(db=2),跨 Worker 消息同步
|
||
- 微信登录状态使用 Redis 存储(db=0),所有 Worker 共享
|
||
"""
|
||
|
||
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 总并发
|
||
|
||
# Worker 类型 - 使用 geventwebsocket 支持 WebSocket
|
||
# Flask-SocketIO 需要异步 worker 来支持 WebSocket
|
||
worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker'
|
||
|
||
# Worker 连接数(gevent 异步模式下可以处理大量并发连接)
|
||
worker_connections = 2000
|
||
|
||
# 每个 worker 处理的最大请求数(防止内存泄漏)
|
||
# 对于 WebSocket 长连接,设置一个较大的值,不能是 0(否则内存泄漏无法恢复)
|
||
max_requests = 10000 # 处理 10000 个请求后重启 worker
|
||
max_requests_jitter = 1000 # 随机抖动,避免所有 worker 同时重启
|
||
|
||
# ==================== 超时配置 ====================
|
||
|
||
# Worker 超时时间(秒),WebSocket 需要长连接,设大一些
|
||
timeout = 300
|
||
|
||
# 优雅关闭超时时间(秒)
|
||
graceful_timeout = 30
|
||
|
||
# 保持连接超时时间(秒)
|
||
keepalive = 65
|
||
|
||
# ==================== SSL 配置 ====================
|
||
|
||
cert_file = '/etc/letsencrypt/live/valuefrontier.cn/fullchain.pem'
|
||
key_file = '/etc/letsencrypt/live/valuefrontier.cn/privkey.pem'
|
||
|
||
if os.path.exists(cert_file) and os.path.exists(key_file):
|
||
certfile = cert_file
|
||
keyfile = key_file
|
||
|
||
# ==================== 日志配置 ====================
|
||
|
||
accesslog = '-'
|
||
errorlog = '-'
|
||
loglevel = 'debug' # 调试时用 debug,正常运行用 info
|
||
capture_output = True # 捕获 print 输出到日志
|
||
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)sμs'
|
||
|
||
# ==================== 进程管理 ====================
|
||
|
||
daemon = False
|
||
pidfile = '/tmp/gunicorn_app.pid'
|
||
proc_name = 'vf_react_app'
|
||
|
||
# 不预加载应用,确保 gevent monkey patch 正确
|
||
preload_app = False
|
||
|
||
# ==================== Hook 函数 ====================
|
||
|
||
def on_starting(server):
|
||
"""服务器启动时调用"""
|
||
print("=" * 70)
|
||
print("🚀 Gunicorn + Flask-SocketIO 多进程服务器正在启动...")
|
||
print(f" Workers: {server.app.cfg.workers}")
|
||
print(f" Worker Class: {server.app.cfg.worker_class}")
|
||
print(f" Bind: {server.app.cfg.bind}")
|
||
print(f" Worker Connections: {server.app.cfg.worker_connections}")
|
||
print(f" Max Requests: {server.app.cfg.max_requests}")
|
||
print("-" * 70)
|
||
print(" Redis 存储分配:")
|
||
print(" - db=0: 微信登录状态")
|
||
print(" - db=1: Flask Session")
|
||
print(" - db=2: SocketIO 消息队列")
|
||
print("=" * 70)
|
||
|
||
|
||
def when_ready(server):
|
||
"""服务准备就绪时调用"""
|
||
print(f"✅ Gunicorn 服务准备就绪! {server.app.cfg.workers} 个 Worker 已启动")
|
||
print(" 多进程支持已启用 (Redis Session + SocketIO Message Queue)")
|
||
|
||
|
||
def post_worker_init(worker):
|
||
"""Worker 初始化完成后调用"""
|
||
# gevent monkey patching 在这里自动完成
|
||
print(f"✅ Worker {worker.pid} 已初始化 (gevent 异步模式, 可处理 2000 并发连接)")
|
||
|
||
|
||
def worker_abort(worker):
|
||
"""Worker 收到 SIGABRT 信号时调用(超时)"""
|
||
print(f"⚠️ Worker {worker.pid} 超时被终止,正在重启...")
|
||
|
||
|
||
def on_exit(server):
|
||
"""服务器退出时调用"""
|
||
print("🛑 Gunicorn 服务器已关闭")
|
||
|
||
|
||
# ==================== systemd 服务配置示例 ====================
|
||
"""
|
||
保存为 /etc/systemd/system/vf_react.service:
|
||
|
||
[Unit]
|
||
Description=VF React Flask Application with SocketIO
|
||
After=network.target redis.service mysql.service
|
||
|
||
[Service]
|
||
User=root
|
||
Group=root
|
||
WorkingDirectory=/path/to/vf_react
|
||
Environment="PATH=/path/to/venv/bin"
|
||
ExecStart=/path/to/venv/bin/gunicorn -c gunicorn_app_config.py app:app
|
||
ExecReload=/bin/kill -s HUP $MAINPID
|
||
Restart=always
|
||
RestartSec=5
|
||
TimeoutStopSec=30
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
|
||
启用服务:
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable vf_react
|
||
sudo systemctl start vf_react
|
||
sudo systemctl status vf_react
|
||
|
||
查看日志:
|
||
sudo journalctl -u vf_react -f
|
||
"""
|
||
|