Discord API Websocket网关运行一段时间后自动断开问题求助
Discord网关不定时断开问题排查与解决方案
问题根源梳理
- 参数拼写错误:网关连接地址里的
encording是错误拼写,正确应为encoding,错误的传输格式参数会导致Discord服务端不定时主动断开连接。 - 序列数更新逻辑错误:你当前仅在匹配到指定 guild、channel 的消息事件时才更新
last_sequence,其余OP0 Dispatch事件的序列数全部被忽略。Discord要求网关客户端必须跟踪所有事件的序列数,心跳包携带的序列数不正确会被服务端判定为异常连接,直接踢下线。 - 心跳任务管理异常:用
asyncio.ensure_future创建的心跳任务没有被持有引用,当WebSocket连接断开、ws对象被销毁时,心跳任务仍在后台运行,尝试向已销毁的连接对象发送数据,就会触发Task was destroyed but it is pending!报错。 - OP7重连逻辑完全错误:Discord下发OP7指令是要求客户端立即断开当前连接,再用之前保存的会话ID、序列数发起新连接后发送Resume指令。你当前直接在即将关闭的旧连接上发送Resume指令,完全不符合协议要求,自然无法重连成功。
- Resume参数错误:Resume需要传入之前连接成功后
Ready事件返回的session_id,你写死为null,即使发起重连也会失败。
解决步骤
- 修正网关连接地址拼写,把
wss://gateway.discord.gg/?v=9&encording=json改为wss://gateway.discord.gg/?v=9&encoding=json - 调整
last_sequence更新逻辑,所有OP0事件都更新序列数:
elif data["op"] == 0: # Dispatch last_sequence = data['s'] # 所有Dispatch事件先更新序列数 try: if data['d']['guild_id']==("669653521007902751") and data['d']['channel_id']==("669653521007902766"): print(f"{data['d']['content']}") except: pass
- 新增会话ID存储,收到
Ready事件时保存session_id:
# 在OP0 Dispatch分支里新增 if data['t'] == 'READY': session_id = data['d']['session_id'] # 全局存储这个session_id供Resume使用
- 优化心跳任务管理,持有任务引用,连接断开时主动取消:
# 全局或者main函数内定义心跳任务变量 heartbeat_task = None # 收到Hello创建任务时保存引用 heartbeat_task = asyncio.create_task(heartbeat(ws, data['d']['heartbeat_interval'])) # 连接断开时(比如OP7收到后、或者循环退出时)取消任务 if heartbeat_task and not heartbeat_task.done(): heartbeat_task.cancel() try: await heartbeat_task except asyncio.CancelledError: pass
- 修正OP7处理逻辑,不要在旧连接操作,直接触发重连流程:
elif data["op"] == 7: # Reconnect print("收到重连指令,准备断开重连") # 取消心跳任务 if heartbeat_task and not heartbeat_task.done(): heartbeat_task.cancel() # 断开当前连接,外层加重试循环发起新连接,新连接建立后发Resume payload break
- 外层增加重连循环,断开后自动发起新连接,Resume失败则重新Identify。
内容的提问来源于stack exchange,提问作者TheCoffee
相关产品推荐
相关产品推荐

