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

Flask-SocketIO等待队列匹配玩家时触发KeyError线程错误求助

全局匹配队列中join_room抛出KeyError的解决方案

问题背景

两名客户端可选择在线游玩:
2 clients can choose to play online
具体流程为加入全局等待队列:
To join a global waiting queue

  • 客户端每秒向服务器发送join_random()请求,查询是否有可加入的房间,前端代码:
function joinRandomGame() {
    socket.emit('join_random', 
                { game    : window.roomVariables.game, 
                  user_id : window.roomVariables.user_id });

setInterval(joinRandomGame, 1000); }
  • 服务器端join_random事件处理逻辑:检查全局等待队列,若队列中玩家数≥2,则创建房间并通过socket通知客户端进入游戏,后端代码:
from threading import Lock
from collections import deque

userQueueLock = Lock()
userQueue = deque()

@socketio.on('join_random')
def handle_join_random(data):
    game = data['game']
    user_id = data['user_id']
    print(userQueue)

    with userQueueLock:
        if user_id not in userQueue:
            userQueue.append(user_id)

        if len(userQueue) >= 2:
            player1 = userQueue.popleft()
            player2 = userQueue.popleft()

            room_id = generate_random_key()              
            room = Room(
                id=room_id,
                users=[player1, player2],    
                points=[0, 0],
                ...
            )

            db.session.add(room)
            db.session.commit()

            # 此处触发线程错误
            join_room(room_id, sid=player1) 
            join_room(room_id, sid=player2)

            emit('play_game',{'game': game, 'room_id': room_id}, room=room_id)

错误现象

当两名客户端都进入队列后,调用join_room时抛出KeyError,错误日志如下:

deque(['user_1709895093301', 'user_1707675752654'])
Exception in thread Thread-104 (_handle_event_internal):
Traceback (most recent call last):
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/socketio/server.py", line 586, in _handle_event_internal
    r = server._trigger_event(data[0], namespace, sid, *data[1:])
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/socketio/server.py", line 611, in _trigger_event
    return handler(*args)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/flask_socketio/__init__.py", line 282, in _handler
    return self._handle_event(handler, message, namespace, sid,
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/flask_socketio/__init__.py", line 827, in _handle_event
    ret = handler(*args)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/socketio_events.py", line 125, in handle_join_random
    join_room(room_id, sid=player1)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/flask_socketio/__init__.py", line 1035, in join_room
    socketio.server.enter_room(sid, room, namespace=namespace)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/socketio/server.py", line 282, in enter_room
    self.manager.enter_room(sid, namespace, room)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/socketio/manager.py", line 71, in enter_room
    return self.basic_enter_room(sid, namespace, room, eio_sid=eio_sid)
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/socketio/base_manager.py", line 112, in basic_enter_room
    eio_sid = self.rooms[namespace][None][sid]
  File "/home/taylor/Desktop/Projects/web_dev/Projects/Flask/Tic-Tac-Toe-Web/venv/lib/python3.10/site-packages/bidict/_base.py", line 523, in __getitem__
    return self._fwdm[key]
KeyError: 'user_1709895093301'

问题原因

  1. 混淆user_id与socket sid:代码中把自定义业务层的user_id当作SocketIO的连接会话ID(sid)传入join_room,但join_room需要的是SocketIO内部用于追踪活跃连接的sid,找不到对应sid就会抛出KeyError。
  2. 重复请求导致队列异常:客户端每秒发送一次请求,可能导致同一用户多次加入队列,后续处理时对应的连接可能已断开或sid关联失效。

解决方案

方案1:正确传递socket sid

前端发送请求时传递SocketIO内置的sid(通过socket.id获取),后端队列存储sid而非user_id:

前端修改:

let joinInterval;
function joinRandomGame() {
    socket.emit('join_random', { 
        game: window.roomVariables.game, 
        sid: socket.id  // 传递SocketIO会话ID
    });
}
// 启动定时请求
joinInterval = setInterval(joinRandomGame, 1000);

后端修改:

from threading import Lock
from collections import deque

userQueueLock = Lock()
userQueue = deque()

@socketio.on('join_random')
def handle_join_random(data):
    game = data['game']
    current_sid = data['sid']
    print(userQueue)

    with userQueueLock:
        # 避免同一连接重复加入队列
        if current_sid not in userQueue:
            userQueue.append(current_sid)

        if len(userQueue) >= 2:
            player1_sid = userQueue.popleft()
            player2_sid = userQueue.popleft()

            room_id = generate_random_key()              
            room = Room(
                id=room_id,
                # 若需存储用户ID,需从sid关联的会话中获取
                users=[get_user_id_by_sid(player1_sid), get_user_id_by_sid(player2_sid)],    
                points=[0, 0],
                ...
            )

            db.session.add(room)
            db.session.commit()

            # 使用正确的sid调用join_room
            join_room(room_id, sid=player1_sid) 
            join_room(room_id, sid=player2_sid)

            emit('play_game',{'game': game, 'room_id': room_id}, room=room_id)

# 辅助函数:通过sid获取用户ID(需结合自身用户会话实现)
def get_user_id_by_sid(sid):
    from flask import session
    return session.get('user_id', None)

方案2:清理断开连接的用户

监听socket_disconnect事件,将断开的连接从队列中移除,避免无效sid残留:

@socketio.on('disconnect')
def handle_disconnect():
    with userQueueLock:
        if request.sid in userQueue:
            userQueue.remove(request.sid)

方案3:停止重复请求

客户端收到匹配成功的play_game事件后,立即停止发送join_random请求:

// 在前端添加监听逻辑
socket.on('play_game', () => {
    clearInterval(joinInterval);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:34:57