遵循SOLID原则,客户端如何处理无序服务端交互及请求响应关联?
基于SOLID原则的异步WebSocket客户端设计方案(Discord Bot重构)
核心需求梳理
- 异步WebSocket通信:处理
Get/GetDataPackage请求的乱序响应(通过Retrieved消息的key关联请求) - 兼容服务器主动推送的
PrintJSON/RoomUpdate等消息,确保无丢失 - 启动阶段批量同步数据,跟踪大量待处理请求
- 严格遵循SOLID设计原则
遵循SOLID原则的模块拆分与设计思路
1. 单一职责原则:拆分独立业务模块
将不同职责解耦为独立组件,避免单体耦合:
- 请求管理器:仅负责请求的创建、待处理请求跟踪、响应与请求的匹配关联
- 消息处理器集群:按消息类型拆分,一类处理请求对应的
Retrieved响应,一类处理服务器主动推送的消息 - WebSocket适配器:仅封装底层WebSocket的连接、发送、接收逻辑,不介入业务处理
- Discord适配器:仅处理Discord侧的交互逻辑,与服务器通信逻辑完全隔离
2. 开放封闭原则:可扩展的消息处理机制
采用策略模式实现消息的可扩展处理:
- 定义抽象的
MessageHandler接口,所有消息类型(Retrieved/PrintJSON/RoomUpdate)对应独立的实现类 - 维护一个消息处理器注册表,收到消息后根据类型分发到对应处理器;新增消息类型只需添加新的实现类,无需修改现有核心代码
3. 里氏替换原则:统一请求/响应抽象
- 定义抽象
BaseRequest基类,所有请求(Get/GetDataPackage)继承该类,包含唯一标识(如协议指定的key)、请求类型、异步回调逻辑 - 定义抽象
BaseMessage基类,覆盖所有响应与推送消息,包含消息类型、关联标识(如Retrieved的key)
4. 接口隔离原则:细粒度接口拆分
- 为WebSocket适配器拆分
WebSocketSender和WebSocketReceiver接口,分别负责消息发送与接收,避免组件依赖不需要的方法 - 为请求管理器拆分
RequestTracker和RequestMatcher接口,分别负责待处理请求的跟踪与响应匹配
5. 依赖倒置原则:面向抽象编程
- 消息处理器依赖抽象的
BaseMessage接口,而非具体的WebSocket消息格式 - 请求管理器依赖抽象的
BaseRequest和BaseMessage接口,而非具体的Get请求或Retrieved响应实现
核心功能实现细节
待处理请求跟踪与响应关联
- 使用异步安全字典(结合
asyncio.Lock保护普通字典,或使用aiodict)存储待处理请求:key为请求的key,value为asyncio.Future对象(用于异步等待响应) - 发送请求流程:生成唯一
key→ 创建Future并存入字典 → 发送WebSocket消息 - 响应匹配流程:收到
Retrieved消息后,提取key→ 从字典中取出对应Future→ 设置Future结果,完成异步等待 - 超时处理:为每个
Future设置超时时间,超时后自动从字典移除并触发错误回调
服务器主动推送消息的处理
- WebSocket接收器收到消息后,先判断是否为带
key的响应消息:- 若是响应消息,转发给
RetrievedHandler处理关联逻辑 - 若为主动推送消息(无对应请求
key),直接分发到对应处理器(如PrintJSONHandler/RoomUpdateHandler)
- 若是响应消息,转发给
- 使用异步队列(
asyncio.Queue)缓存所有收到的消息,消息处理器从队列中取消息处理,避免消息丢失,同时实现收发逻辑解耦
启动阶段批量数据同步
- 使用
asyncio.Semaphore控制并发请求数量,避免服务器过载 - 批量创建请求并生成对应的
Future,通过asyncio.gather等待所有请求完成;等待过程中仍可正常处理服务器推送的消息,不阻塞Bot的其他功能
简化代码示例
请求管理器核心实现
import asyncio from typing import Dict, Optional class RequestManager: def __init__(self): self._pending_requests: Dict[str, asyncio.Future] = {} self._lock = asyncio.Lock() async def track_request(self, request_key: str) -> asyncio.Future: async with self._lock: future = asyncio.Future() self._pending_requests[request_key] = future return future async def match_response(self, response_key: str, response_data): async with self._lock: future = self._pending_requests.pop(response_key, None) if future and not future.done(): future.set_result(response_data)
消息处理器抽象与实现
from abc import ABC, abstractmethod class MessageHandler(ABC): @abstractmethod async def handle(self, message: dict): pass class RetrievedHandler(MessageHandler): def __init__(self, request_manager: RequestManager): self.request_manager = request_manager async def handle(self, message: dict): request_key = message.get("key") await self.request_manager.match_response(request_key, message) class PrintJSONHandler(MessageHandler): async def handle(self, message: dict): # 示例:将消息转发到Discord print(f"处理PrintJSON消息: {message}")
内容的提问来源于stack exchange,提问作者T. P
相关产品推荐
相关产品推荐

