Flask 与 SocketIO 正确初始化及最佳实践调试

1、问题

我使用Flask和Flask-SocketIO 来做 Websocket 链接。前期正常使用,但是后期布置修改什么导致Websocket连接失败。排查需求,才发现初始化不正常导致。

SocketIO 和 Flask 应用的初始化顺序和引用循环的问题

2、环境

bash 复制代码
python-engineio==4.11.1
python-socketio==5.12.0
Flask-SocketIO==5.3.6

3、正常初始化【单文件】

bash 复制代码
from flask import Flask, render_template
from flask_socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app)  # 初始化 SocketIO

# 默认路由,用于渲染 HTML 页面
@app.route('/')
def index():
    return render_template('index.html')

# 出现消息后,率先执行此处
@socketio.on("message", namespace="/ws")
def socket(message):
    print(f"接收到消息: {message}")
    for i in range(1, 10):
        socketio.sleep(1)
        print(f"发送消息: {i}")
        socketio.emit("response",           # 绑定通信
                      {"data": i},           # 返回socket数据
                      namespace="/ws")


# 当websocket连接成功时,自动触发connect默认方法
@socketio.on("connect", namespace="/ws")
def connect():
    print("链接建立成功..")


# 当websocket连接失败时,自动触发disconnect默认方法
@socketio.on("disconnect", namespace="/ws")
def disconnect():
    print("链接建立失败..")

if __name__ == '__main__':
    socketio.run(app, debug=True, host='0.0.0.0', port=5000,allow_unsafe_werkzeug=True)

4、多文件初始化【两个文件】

主要是 manage.pyinit .py 两个文件。

app/init.py

bash 复制代码
from flask import Flask
from flask_socketio import SocketIO
from flasgger import Swagger
from flask_cors import CORS
from flask_migrate import Migrate
from app.config import config
from app.extension import db

# 创建一个全局的 socketio 对象,但不立即初始化
socketio = SocketIO()

# 其他 swagger 配置保持不变...

def create_app(DevelopmentConfig=None):
    if DevelopmentConfig is None:
        DevelopmentConfig = 'development'

    app = Flask(__name__)
    app.config['SECRET_KEY'] = '123456'
    
    # 加载配置项
    app.config.from_object(config.get(DevelopmentConfig))
    
    from app.api import config_blueprint
    # 注册蓝图
    config_blueprint(app)
    
    # 数据库配置初始化
    config_extensions(app)
    
    # 数据库迁移相关代码...
    from app.api.models.EsModels import sysEsLogs,sysCretitLogs
    from app.api.models.NetModels import sysNetLogs
    from app.api.models.UpsModels import sysUpsLogs
    migrate = Migrate(app, db)
    
    # Swagger初始化
    Swagger(app, config=swagger_config, template=swagger_template)
    
    # CORS配置
    CORS(app, resources={r'/*': {'origins': '*'}}, supports_credentials=True)
    
    # 初始化 SocketIO
    socketio.init_app(app, cors_allowed_origins="*")
    
    return app

socket_events.py

bash 复制代码
from app import socketio

# Socket.IO 事件处理
@socketio.on("message", namespace="/ws")
def socket(message):
    print(f"接收到消息: {message}")
    for i in range(1, 10):
        socketio.sleep(1)
        print(f"发送消息: {i}")
        socketio.emit("response",
                     {"data": i},
                     namespace="/ws")

@socketio.on("connect", namespace="/ws")
def connect():
    print("链接建立成功..")

@socketio.on("disconnect", namespace="/ws")
def disconnect():
    print("链接建立失败..") 

manage.py

bash 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024/5/2 12:01
# @Author  : 南宫乘风
# @Email   : [email protected]
# @File    : manage.py
# @Software: PyCharm
import atexit
import os
import sys

from apscheduler.schedulers.background import BackgroundScheduler

from app import create_app, socketio
from app.common.util.LogHandler import log
from app.crontab.NetCronTab import NetworkMonitor
from app.crontab.UpsCronTab import UPSMonitor
from app.socket_events import *
# 默认为开发环境,按需求修改 production  development
config_name = 'development'

app = create_app(config_name)
# 解决中文乱码
# app.json.ensure_ascii = False

# from skywalking import agent, config
# config.init(agent_collector_backend_services='192.168.82.105:11800', agent_name='python-flask@devTenant',agent_instance_name="python-flask")
# agent.start()

# 数据库迁移
def start_scheduler():
    # 创建一个后台调度器
    scheduler = BackgroundScheduler(timezone="Asia/Shanghai")
    from app.crontab.EsCronTab import EsIndexCron
    scheduler.add_job(func=EsIndexCron, trigger='interval', minutes=1)
    log.info("EsIndexCron 定时任务启动成功")


    # scheduler.add_job(func=send_alert, trigger="interval", seconds=20)
    # 启动调度器
    scheduler.start()
    atexit.register(lambda: scheduler.shutdown())


if __name__ == '__main__':
    # 获取当前文件的绝对路径
    current_file = os.path.abspath(__file__)
    base_dir = os.path.dirname(current_file)
    # 将项目目录添加到 sys.path
    if base_dir not in sys.path:
        sys.path.append(base_dir)
    if not app.debug or app.testing:
        start_scheduler()
        log.info("定时任务启动成功")
    socketio.run(app, debug=True, use_reloader=True, host='0.0.0.0', port=5001)
    # use_reloader=False, threaded=True,

在postman输入地址和监听事件


相关推荐
Asthenia041241 分钟前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9651 小时前
ovs patch port 对比 veth pair
后端
Asthenia04121 小时前
Java受检异常与非受检异常分析
后端
uhakadotcom1 小时前
快速开始使用 n8n
后端·面试·github
JavaGuide1 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz9652 小时前
qemu 网络使用基础
后端
Asthenia04122 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04122 小时前
Spring 启动流程:比喻表达
后端
Asthenia04123 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua3 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫