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

Gunicorn+Eventlet环境下SocketIO后台任务Redis Pub/Sub订阅无故停止问题求助

Gunicorn+Eventlet环境下SocketIO后台任务Redis Pub/Sub订阅无故停止问题求助

大家好,我最近碰到一个头疼的问题,想请教下有没有同行遇到过类似的情况。

我当前的服务架构是:用Gunicorn启动1个Eventlet worker,通过SocketIO和前端做WebSocket通信;同时需要监听一个外部Pod发出的Redis Pub/Sub事件,所以我用SocketIO的后台线程来启动Redis订阅监听逻辑。

以下是我的关键代码:

1. SocketIO初始化(extensions.py)

socketio = SocketIO(
    logger=False, 
    engineio_logger=False, 
    path='/socket.io', 
    cors_allowed_origins='*', 
    async_mode='eventlet', 
    ping_timeout=10, 
    ping_interval=60
)

2. Redis订阅监听类

class RedisConnection:
    def __init__(self):
        # 初始化逻辑省略
        pass

    def subscription_listener(self):
        while True:
            try:
                pubsub = self.redis.pubsub()
                pubsub.subscribe(self.websocket_channel)
                try:
                    for message in pubsub.listen():
                        try:
                            m = TypeAdapter(RedisEnvelopeMessage).validate_python(message)
                            self.logger.debug(f"Received from REDIS: {message}")
                            if m.type == 'message':
                                d = m.data
                                self.logger.debug(f"Received message from REDIS: {d}")
                                with self.app.app_context():
                                    # 从Flask扩展中获取对应的WS服务实例
                                    WSS = self.app.extensions.get(d.module, "RedisWS").ws_services.get(d.company_id)
                                    # 根据消息类型调用对应方法
                                    if d.message is not None:
                                        if isinstance(d.message, list):
                                            getattr(WSS, d.method)(*d.message)
                                        elif isinstance(d.message, dict):
                                            getattr(WSS, d.method)(d.message)
                                        elif isinstance(d.message, str):
                                            getattr(WSS, d.method)(d.message)
                                        else:
                                            getattr(WSS, d.method)()
                            elif m.type == 'subscribe':
                                self.logger.info(f"Subscribed to REDIS channel: {m.channel}")
                            else:
                                self.logger.info(f"Received message from REDIS but NOT PROCESSED: {message}")
                        except Exception as e:
                            self.logger.catch_exception(e)
                            self.logger.error(f"Pubsub parsing error: {e}").save("pubsub_listener")
                except Exception as e:
                    self.logger.catch_exception(e, level="critical")
                    self.logger.error(f"Pubsub listener error: {e}").save("pubsub_listener")
                    # socketio.sleep(2)
            except Exception as e:
                self.logger.catch_exception(e, level="critical")
                self.logger.error(f"Pubsub loop error: {e}").save("pubsub_listener")

3. Flask应用初始化时启动后台任务

def create_app(config_class=Config):
    # 其他初始化逻辑省略
    socketio.init_app(app)
    with app.app_context():
        app.extensions["pubsub"] = socketio.start_background_task(redis_db_static.subscription_listener)

问题现象

代码刚启动时一切正常,Redis订阅能正常接收并处理事件,但运行一段时间后,这个订阅监听就毫无征兆地停止了,而且我检查了所有日志,没有任何异常信息输出。重启Gunicorn服务后,订阅功能又能恢复正常。

我原本期望这个后台线程能持续运行,直到Gunicorn主进程被杀死或重启时才会终止重启,但现在它会中途挂掉,完全找不到原因。

有没有朋友遇到过类似的问题?麻烦给点排查思路或者解决方案,非常感谢!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:57:59