You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask-SocketIO持续推送数据的方案是否合理?如何优化?

Hey there! Let's dive into your Flask-SocketIO continuous data streaming issue. Your current approach works but has some critical flaws that are likely causing your server slowdowns—let's fix that with a cleaner, more robust implementation.

What's Wrong with Your Current Setup?

Your decorator-based loop has a few red flags:

  • Unsafe global state: The runningUserSids set isn't protected by any synchronization, which can lead to race conditions in a coroutine-heavy eventlet environment. In multi-worker deployments (like with Gunicorn), this global set won't sync across workers at all.
  • Resource leaks: If a client disconnects unexpectedly, your loop might not exit cleanly, leaving zombie coroutines running in the background and eating up CPU/memory over time.
  • Redundant timeout logic: The callback wait + timeout combination is unnecessary and adds complexity that can lead to unhandled edge cases.

A Better, Official Approach

Flask-SocketIO provides built-in tools for background tasks that are designed to play nicely with its eventlet/gevent runtime. Here's a refined implementation that avoids the pitfalls of your current code:

import eventlet
eventlet.monkey_patch()
from flask import session, request
from flask_socketio import SocketIO, start_background_task
import functools

# Initialize your SocketIO instance (adjust cors settings as needed)
socketio = SocketIO(cors_allowed_origins="*")

# Thread-safe storage for active background tasks (per client sid)
active_tasks = {}
task_lock = eventlet.Lock()

def stop_client_task(sid):
    """Gracefully stop the background task for a disconnected client"""
    with task_lock:
        task = active_tasks.pop(sid, None)
    if task:
        task.cancel()

def continuous_emit(emit_event, frequency=0.5):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            sid = request.sid
            
            # Prevent duplicate tasks for the same client
            with task_lock:
                if sid in active_tasks:
                    return {"result": "info", "data": "Stream already active for this connection"}
            
            def background_stream_task():
                try:
                    while True:
                        # Fetch fresh data using your existing logic
                        data_output = func(*args, **kwargs)
                        
                        # Emit data and confirm client receipt
                        # `callback=True` returns False if the client is disconnected
                        if not socketio.emit(emit_event, data_output, room=sid, callback=True):
                            break  # Exit loop if client is gone
                        
                        eventlet.sleep(frequency)
                except eventlet.CancelledError:
                    # Expected when task is cancelled on disconnect
                    pass
                except Exception as e:
                    # Notify client of errors and clean up
                    error_msg = f"Data stream error: {str(e)}"
                    socketio.emit("stream_error", {"result": "error", "data": error_msg}, room=sid)
                finally:
                    stop_client_task(sid)
                    print(f"Stopped data stream for client {sid}")
            
            # Start the official background task (managed by Flask-SocketIO)
            task = start_background_task(background_stream_task)
            with task_lock:
                active_tasks[sid] = task
            
            return {"result": "success", "data": "Continuous data stream started"}
        return wrapper
    return decorator

# Handle client disconnects to stop their task immediately
@socketio.on("disconnect")
def handle_client_disconnect():
    sid = request.sid
    stop_client_task(sid)
    print(f"Client {sid} disconnected, cleaned up their stream task")

# Your existing WebSocket login decorator (adjust as needed)
def login_required_ws(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        user_id = session.get("user_id")
        if not user_id:
            socketio.emit("auth_error", {"result": "error", "data": "Authentication required"}, room=request.sid)
            return
        return func(*args, **kwargs)
    return wrapper

# Your data endpoint with the improved decorator
@socketio.on('get_info_test')
@continuous_emit('info_test', frequency=0.5)
@login_required_ws
def get_info():
    result = {"result": None, "data": None}
    try:
        result["data"] = get_test_data(session.get("user_id"))
        result["result"] = "success"
    except Exception as e:
        result["result"] = f"Failed to fetch data: {str(e)}"
    finally:
        return result

# Example data fetch function (replace with your actual logic)
def get_test_data(user_id):
    return {"user_id": user_id, "timestamp": eventlet.time.time()}

Key Improvements in This Implementation

  • Official background tasks: Uses start_background_task which is integrated with Flask-SocketIO's runtime, ensuring proper coroutine management and cleanup.
  • Thread-safe state management: The active_tasks dictionary is protected by an eventlet lock, preventing race conditions when starting/stopping tasks.
  • Clean disconnect handling: When a client disconnects, their task is immediately cancelled and removed from the active list, eliminating resource leaks.
  • Simplified client confirmation: Using callback=True with emit lets us directly detect if the client is still connected, removing the need for manual timeout logic.
  • Better error handling: Catches exceptions during data fetching and notifies the client, while ensuring the task still cleans up properly.

This approach will keep your server stable even with multiple concurrent clients, avoiding the slowdowns you experienced with your original setup.

内容的提问来源于stack exchange,提问作者Xosrov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 16:57:50