update pay ui

This commit is contained in:
2025-12-11 22:36:02 +08:00
parent 54c4f64a49
commit 198f456655
3 changed files with 54 additions and 19 deletions

27
app.py
View File

@@ -54,6 +54,7 @@ from clickhouse_driver import Client as Cclient
from elasticsearch import Elasticsearch
from flask_cors import CORS
import redis
from flask_session import Session
from collections import defaultdict
from functools import lru_cache
@@ -286,15 +287,30 @@ MAIL_DEFAULT_SENDER = 'admin@valuefrontier.cn'
# 重要:生产环境请使用环境变量配置,不要硬编码
import os
app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'vf_production_secret_key_2024_valuefrontier_cn')
app.config['SESSION_COOKIE_SECURE'] = False # 如果生产环境使用HTTPS应设为True
# ============ Redis Session 配置(支持多进程/多 Worker============
# 使用 Redis 存储 session确保多个 Gunicorn worker 共享 session
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.Redis(host='localhost', port=6379, db=1) # 使用 db=1与微信 session 的 db=0 分开
app.config['SESSION_PERMANENT'] = True
app.config['SESSION_USE_SIGNER'] = True # 对 session cookie 签名,提高安全性
app.config['SESSION_KEY_PREFIX'] = 'vf_session:' # session key 前缀
# ============ Redis Session 配置结束 ============
# Cookie 配置 - 重要HTTPS 环境必须设置 SECURE=True
app.config['SESSION_COOKIE_SECURE'] = True # 生产环境使用 HTTPS必须为 True
app.config['SESSION_COOKIE_HTTPONLY'] = True # 生产环境应设为True防止XSS攻击
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # 使用'Lax'以平衡安全性和功能性
app.config['SESSION_COOKIE_DOMAIN'] = None # 不限制域名
app.config['SESSION_COOKIE_PATH'] = '/' # 设置cookie路径
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # session持续7天
app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=30) # 记住登录30天
app.config['REMEMBER_COOKIE_SECURE'] = False # 记住登录cookie不要求HTTPS
app.config['REMEMBER_COOKIE_HTTPONLY'] = False # 允许JavaScript访问
app.config['REMEMBER_COOKIE_SECURE'] = True # 生产环境使用 HTTPS必须为 True
app.config['REMEMBER_COOKIE_HTTPONLY'] = True # 防止XSS攻击
# 初始化 Flask-SessionRedis 存储)
Session(app)
print("✅ Flask-Session (Redis) 已初始化,支持多 Worker 共享 session")
# 配置邮件
app.config['MAIL_SERVER'] = MAIL_SERVER
@@ -395,16 +411,21 @@ def _detect_async_mode():
_async_mode = _detect_async_mode()
print(f"📡 Flask-SocketIO async_mode: {_async_mode}")
# Redis 消息队列 URL支持多 Worker 之间的消息同步)
SOCKETIO_MESSAGE_QUEUE = 'redis://localhost:6379/2' # 使用 db=2与 session 和微信 session 分开
socketio = SocketIO(
app,
cors_allowed_origins=["http://localhost:3000", "http://127.0.0.1:3000", "http://localhost:5173",
"https://valuefrontier.cn", "http://valuefrontier.cn"],
async_mode=_async_mode,
message_queue=SOCKETIO_MESSAGE_QUEUE, # 启用 Redis 消息队列,支持多 Worker
logger=True,
engineio_logger=False,
ping_timeout=120, # 心跳超时时间客户端120秒内无响应才断开
ping_interval=25 # 心跳检测间隔每25秒发送一次ping
)
print(f"✅ Flask-SocketIO 已配置 Redis 消息队列,支持多 Worker")
@login_manager.user_loader

View File

@@ -1,14 +1,18 @@
# -*- coding: utf-8 -*-
"""
Gunicorn 配置文件 - app.py 生产环境配置(支持 Flask-SocketIO + WebSocket
Gunicorn 配置文件 - app.py 生产环境配置(支持 Flask-SocketIO + WebSocket + 多进程
使用方式:
# 推荐方式: 使用此配置文件启动
# 推荐方式: 使用此配置文件启动(多 Worker 模式)
gunicorn -c gunicorn_app_config.py app:app
# 如果遇到 502 错误,可以尝试安装 eventlet 后使用:
pip install eventlet
gunicorn -k eventlet -w 1 -b 0.0.0.0:5001 --timeout 300 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
@@ -18,8 +22,10 @@ import os
# 绑定地址和端口
bind = '0.0.0.0:5001'
# Worker 进程数WebSocket 需要单 worker
workers = 1
# 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
@@ -28,9 +34,10 @@ worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker'
# Worker 连接数gevent 异步模式下可以处理大量并发连接)
worker_connections = 2000
# 每个 worker 处理的最大请求数
max_requests = 0 # 禁用自动重启,避免 WebSocket 连接中断
max_requests_jitter = 0
# 每个 worker 处理的最大请求数(防止内存泄漏)
# 对于 WebSocket 连接,设置一个较大的值,不能是 0否则内存泄漏无法恢复
max_requests = 10000 # 处理 10000 个请求后重启 worker
max_requests_jitter = 1000 # 随机抖动,避免所有 worker 同时重启
# ==================== 超时配置 ====================
@@ -73,24 +80,31 @@ preload_app = False
def on_starting(server):
"""服务器启动时调用"""
print("=" * 60)
print("🚀 Gunicorn + Flask-SocketIO 服务器正在启动...")
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("=" * 60)
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("✅ Gunicorn 服务准备就绪! WebSocket 支持已启")
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 异步模式)")
print(f"✅ Worker {worker.pid} 已初始化 (gevent 异步模式, 可处理 2000 并发连接)")
def worker_abort(worker):

View File

@@ -6,7 +6,7 @@ Flask-Compress==1.14
Flask-SocketIO==5.3.6
Flask-Mail==0.9.1
Flask-Migrate==4.0.5
Flask-Session==0.5.0
Flask-Session==0.8.0
redis==5.0.1
pandas==2.0.3
numpy==1.24.3