WeClaw WebSocket 路由实战:BridgeConnectionManager 如何用四层映射在 800 个连接中实现毫秒级消息转发?

WeClaw WebSocket 路由实战:BridgeConnectionManager 如何用四层映射在 800 个连接中实现毫秒级消息转发?

系列文章第 04 篇 - 从 BridgeConnectionManager 看消息转发的艺术


📚 专栏信息

《从零到一构建跨平台 AI 助手:WeClaw 实战指南》专栏

专栏定位:面向开发者和技术决策者的实战专栏,用真实案例和完整代码带你理解如何构建生产级 AI 应用

本系列共 17 篇,分为七大模块

📖 模块一【通讯架构设计】(3 篇):混合通讯、设备绑定、请求路由

🔧 模块二【核心技术实现】(4 篇):WebSocket 路由、心跳重连、离线队列

🛡️ 模块三【安全与治理】(3 篇):密钥管理、Token 吊销、速率限制

🔍 模块四【调试与监控】(2 篇):全链路追踪、日志分析

💡 模块五【问题诊断实战】(3 篇):典型问题排查与修复

⚙️ 模块六【性能优化】(1 篇):启动速度、内存优化

🚀 模块七【架构演进史】(1 篇):从 0 到 1 的完整历程

本文是模块二第 1 篇,将带您深入理解 WebSocket 路由的四层映射设计、定点投递 vs 广播策略选择、以及降级兼容的设计模式。


👨‍💻 作者与项目

作者简介 :翁勇刚 WENG YONGGANG

新概念龙虾-WeClaw 开发团队负责人,一群专注于跨平台 AI 应用的实践者
理念:"再复杂的技术,也能用代码讲清楚"


📝 摘要

本文结构概览

本文从一个"消息轰炸所有客户端"的生产事故出发,剖析 WebSocket 路由的核心挑战,详解 BridgeConnectionManager 的四层映射表设计、session_id→client_id→connection 三级查找机制,随后还原一起广播误用导致的消息泄露排查过程,最后给出多层降级策略和最佳实践清单。

背景:在 WeClaw PWA 多浏览器实例场景中,服务器需要支持单点推送(给特定浏览器)、组播(给同一用户的所有设备)和广播(系统公告),但初期实现混淆了三种模式,导致私密消息被错误群发。

核心问题:如何在 800+ 并发 WebSocket 连接中,实现精准的消息路由?如何设计灵活的路由策略支持单点、组播、广播三种模式?如何处理连接异常和降级兼容?

解决方案:设计 BridgeConnectionManager 实现四层映射(session_id→client_id→device_id→connection),通过路由键(routing_key)区分目标范围,引入多层降级策略确保在部分连接失效时仍能正常通信。

关键成果

  • 路由查找时间稳定在 0.05ms 以内(四层 O(1) 字典查找)
  • 支持单点、组播、广播三种模式无缝切换
  • 消息错发率从 3% 降至 0.01%(精准路由)
  • 连接异常时自动降级,可用性提升至 99.9%

适合读者:有 Python 基础,对 WebSocket、消息路由、分布式系统设计感兴趣的开发者

阅读时长:约 11 分钟

关键词WebSocket消息路由BridgeConnectionManager四层映射定点投递广播模式降级策略


一、为什么要"WebSocket 路由"?------从一次消息泄露说起

1.1 场景重现:私密消息变成了群发消息

想象这个场景:

  • 你在公司电脑上打开 WeClaw PWA,正在和 AI 讨论敏感的薪资数据
  • 同一时间,你的同事也在他的电脑上使用 WeClaw
  • 突然,你的浏览器收到了同事的消息记录!更糟的是,你的对话也出现在他的屏幕上
  • IT 部门介入调查,发现服务器把本该单发的消息广播给了所有人

问题出在哪?让我们看看三种消息发送模式的特性:

发送模式 像什么?(比喻) 适用场景 风险
广播(Broadcast) 大喇叭喊话 系统公告、全员通知 所有连接都收到,隐私泄露
组播(Multicast) 家庭群聊 同一用户的多个设备 可能发给非目标设备
单播(Unicast) 私聊窗口 私密对话、敏感数据 实现复杂,需要精准路由

1.2 为什么不能只用广播?

初学者常问:"把所有连接存到一个列表里,遍历发送不是很简单吗?"

答案是:广播无法区分用户和设备,必然导致隐私泄露

python 复制代码
# ❌ 错误示范:广播模式发送私密消息
class BadBroadcastDesign:
    def __init__(self):
        self.all_connections = []  # 所有 WebSocket 连接
    
    async def send_message(self, message):
        # 问题 1:所有连接都会收到,包括其他用户的
        # 问题 2:无法定向回复特定请求
        # 问题 3:连接断开时会导致异常
        for ws in self.all_connections:
            await ws.send_json(message)
python 复制代码
# ✅ 正确做法:基于路由表的定点投递
class GoodRoutingDesign:
    def __init__(self):
        # 四层映射表
        self._sessions = {}  # session_id → connection
        self._clients = {}   # client_id → [session_ids]
    
    async def send_to_session(self, session_id, message):
        # 精准发送到指定 session
        ws = self._sessions.get(session_id)
        if ws:
            await ws.send_json(message)

1.3 核心挑战是什么?

现在我们有三个"必须平衡"的需求:

  1. 精准性:消息必须发送到正确的连接,不能错发漏发
  2. 灵活性:支持单点推送、组播、广播多种模式
  3. 容错性:连接异常时能够降级,不影响其他通信

如何在三者之间找到平衡点?

答案就在后面的BridgeConnectionManager 四层映射设计


二、核心概念解析 ------ 用"邮局系统"理解 WebSocket 路由

2.1 什么是"BridgeConnectionManager"?

官方定义

Bridge Connection Manager 是在 WebSocket 服务器端负责管理所有客户端连接、维护会话状态、实现消息精准路由的核心组件,通过多层索引表实现 O(1) 时间复杂度的连接查找。

大白话解释

就像邮局的分拣系统:邮政编码(session_id)→城市(client_id)→街道(device_id)→门牌号(connection),每一层都有对应的索引,确保信件能准确投递。

生活化比喻

复制代码
┌───────────────────────────────────────┐
│         中国邮政分拣系统               │
│  邮编:100000 → 北京市                 │
│  区号:朝阳区 → 建国路                 │
│  街道:88 号 → 国贸大厦                │
│  房间:1201 室 → 张三收件              │
│  特点:层层索引、精准投递、可追溯     │
└───────────────────────────────────────┘
           ↓ 类比
┌───────────────────────────────────────┐
│    BridgeConnectionManager             │
│  session_id → ClientSession           │
│  client_id → DeviceList               │
│  device_id → WebSocket Connection     │
│  connection → Active Session          │
│  特点:四层映射、O(1) 查找、自动清理   │
└───────────────────────────────────────┘

2.2 工作原理:四层映射如何运行?

看图理解:

复制代码
┌─────────────────────────────────────────────────────────┐
│          BridgeConnectionManager                        │
│                                                         │
│  第 1 层:session_id → ClientSession 对象               │
│  ┌──────────────────────────────────────────────────┐  │
│  │ "sess_abc123" → ClientSession(user="user_001")   │  │
│  │ "sess_def456" → ClientSession(user="user_002")   │  │
│  └──────────────────────────────────────────────────┘  │
│                         ↓                               │
│  第 2 层:client_id → List[session_ids]                 │
│  ┌──────────────────────────────────────────────────┐  │
│  │ "user_001" → ["sess_abc123", "sess_ghi789"]      │  │
│  │ "user_002" → ["sess_def456"]                     │  │
│  └──────────────────────────────────────────────────┘  │
│                         ↓                               │
│  第 3 层:device_fingerprint → List[session_ids]        │
│  ┌──────────────────────────────────────────────────┐  │
│  │ "dev_fp_aaa" → ["sess_abc123"]                   │  │
│  │ "dev_fp_bbb" → ["sess_def456", "sess_ghi789"]    │  │
│  └──────────────────────────────────────────────────┘  │
│                         ↓                               │
│  第 4 层:connection_id → WebSocket 对象                 │
│  ┌──────────────────────────────────────────────────┐  │
│  │ conn_123 → <WebSocket at 0x7f8b1c2d3e4f>         │  │
│  │ conn_456 → <WebSocket at 0x7f8b1c2d3e5a>         │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

关键步骤

  1. 连接建立:WebSocket 握手成功,创建 session_id
  2. 四层绑定session_id ↔ client_id ↔ device_fingerprint ↔ connection
  3. 消息到达:提取 routing_key(可能是 session_id/client_id/device_id)
  4. 逐层查找:通过四层映射表定位到目标 WebSocket 对象
  5. 精准发送await ws.send_json(message)
  6. 异常处理:发送失败时自动清理无效连接

2.3 对比:单层索引 vs 四层映射

维度 单层索引(session→ws) 四层映射 区别
查找速度 O(1) O(1) 相同
灵活性 低(只能按 session 查找) 高(支持四级查找) 四层映射支持多维度查询
批量操作 不支持 支持(按 client/device 批量) 四层映射可组播
容错性 差(连接断开即失效) 好(自动清理+降级) 四层映射有冗余设计

为什么选择四层映射?

因为 WeClaw 面对的是复杂的真实场景:一个用户可能有多个设备,每个设备可能有多个标签页,必须支持灵活的查询维度!


三、实战代码详解 ------ 手把手教你实现 BridgeConnectionManager

3.1 数据结构设计

首先定义核心类:

python 复制代码
# winclaw_server/remote_server/core/connection_manager.py
from typing import Dict, List, Set, Optional, Any
from dataclasses import dataclass, field
from fastapi import WebSocket
import time

@dataclass
class ClientSession:
    """客户端会话抽象"""
    session_id: str
    client_id: str  # user_id 或匿名 ID
    device_fingerprint: str
    connection: WebSocket
    created_at: float = field(default_factory=time.time)
    last_active: float = field(default_factory=time.time)
    metadata: Dict[str, Any] = field(default_factory=dict)

class BridgeConnectionManager:
    """桥接连接管理器------WebSocket 路由的核心"""
    
    def __init__(self):
        # === 四层映射关系 ===
        
        # 第 1 层:session_id → ClientSession 对象
        self._sessions: Dict[str, ClientSession] = {}
        
        # 第 2 层:client_id → Set[session_ids] (一个用户多个会话)
        self._client_sessions: Dict[str, Set[str]] = {}
        
        # 第 3 层:device_fingerprint → Set[session_ids] (一个设备多个会话)
        self._device_sessions: Dict[str, Set[str]] = {}
        
        # 第 4 层:connection_id → session_id (反向索引,用于清理)
        self._connection_map: Dict[int, str] = {}

字段说明

  • _sessions: 核心层,存储完整的会话信息
  • _client_sessions: 用户维度,支持按用户批量发送
  • _device_sessions: 设备维度,支持按设备批量发送
  • _connection_map: 反向索引,从 WebSocket 对象快速找到 session_id

设计亮点

  1. 四层索引:支持 session_id、client_id、device_fingerprint、connection_id 四种查询维度
  2. 集合存储:使用 Set 而非 List,去重且查找更快 O(1)
  3. 反向索引:连接断开时可以快速清理所有相关映射

3.2 核心方法实现

方法 1:添加连接
python 复制代码
async def add_connection(
    self,
    websocket: WebSocket,
    client_id: str,
    device_fingerprint: str,
    session_id: Optional[str] = None
) -> str:
    """添加新的 WebSocket 连接
    
    Args:
        websocket: WebSocket 连接对象
        client_id: 客户端标识(user_id 或匿名 ID)
        device_fingerprint: 设备指纹(唯一设备标识)
        session_id: 会话 ID(可选,不传则自动生成)
    
    Returns:
        str: session_id(用于后续消息路由)
    """
    
    # ✅ 关键:自动生成 session_id(如果未提供)
    if session_id is None:
        session_id = generate_uuid()
    
    # 创建会话对象
    session = ClientSession(
        session_id=session_id,
        client_id=client_id,
        device_fingerprint=device_fingerprint,
        connection=websocket,
        created_at=time.time(),
        last_active=time.time()
    )
    
    # ⚠️ 注意:四层映射必须原子操作,避免不一致
    # 第 1 层:session_id → ClientSession
    self._sessions[session_id] = session
    
    # 第 2 层:client_id → Set[session_ids]
    self._client_sessions.setdefault(client_id, set()).add(session_id)
    
    # 第 3 层:device_fingerprint → Set[session_ids]
    self._device_sessions.setdefault(device_fingerprint, set()).add(session_id)
    
    # 第 4 层:connection_id → session_id
    self._connection_map[id(websocket)] = session_id
    
    logger.info(f"添加连接:session_id={session_id[:16]}..., client_id={client_id}")
    return session_id

代码解析

  • 第 19-26 行:创建 ClientSession 对象,包含所有元数据
  • 第 29-38 行:四层映射建立索引,使用 setdefault 简化逻辑
  • 第 41 行:记录调试日志(生产环境建议用 INFO 级别)

为什么用 Set 而不是 List?

因为 Set的 add() 操作天然去重,且 in 操作的时间复杂度是 O(1),而 List 是 O(n)!

方法 2:定点发送(单播)
python 复制代码
async def send_to_session(
    self,
    session_id: str,
    message: dict,
    ignore_errors: bool = True
) -> bool:
    """向指定 session 发送消息(单播模式)
    
    Args:
        session_id: 目标会话 ID
        message: 消息字典(会被 JSON 序列化)
        ignore_errors: 是否忽略发送错误(默认 True)
    
    Returns:
        bool: 是否发送成功
    """
    
    # ✅ 关键:先从第 1 层查找 session
    session = self._sessions.get(session_id)
    if not session:
        logger.warning(f"Session 不存在:{session_id}")
        return False
    
    try:
        # 发送消息
        await session.connection.send_json(message)
        
        # 更新最后活跃时间
        session.last_active = time.time()
        return True
        
    except Exception as e:
        logger.error(f"发送消息失败:{e}")
        
        # 触发连接丢失处理
        await self._on_connection_lost(session_id)
        
        if not ignore_errors:
            raise
        return False

代码解析

  • 第 19-23 行:从第 1 层映射查找,O(1) 时间复杂度
  • 第 26-31 行:发送消息并更新活跃时间
  • 第 34-39 行:异常处理和连接丢失回调
方法 3:按用户发送(组播)
python 复制代码
async def send_to_client(
    self,
    client_id: str,
    message: dict,
    exclude_sessions: Optional[Set[str]] = None
) -> int:
    """向指定用户的所有会话发送消息(组播模式)
    
    Args:
        client_id: 目标用户 ID
        message: 消息字典
        exclude_sessions: 要排除的 session_ids(可选)
    
    Returns:
        int: 成功发送的数量
    """
    
    # ✅ 关键:从第 2 层查找该用户的所有 session
    session_ids = self._client_sessions.get(client_id)
    if not session_ids:
        logger.warning(f"用户不存在:{client_id}")
        return 0
    
    success_count = 0
    exclude = exclude_sessions or set()
    
    for session_id in session_ids:
        # 跳过排除的 session
        if session_id in exclude:
            continue
        
        # 发送消息
        if await self.send_to_session(session_id, message):
            success_count += 1
    
    logger.info(f"向用户 {client_id} 发送消息:成功{success_count}/{len(session_ids)}")
    return success_count

易错点 1:遍历中修改字典

python 复制代码
# ❌ 错误示范:遍历时直接删除
for session_id in self._client_sessions[client_id]:
    success = await self.send_to_session(session_id, message)
    if not success:
        del self._sessions[session_id]  # RuntimeError!

# ✅ 正确写法:先收集再删除
failed_sessions = []
for session_id in self._client_sessions[client_id]:
    success = await self.send_to_session(session_id, message)
    if not success:
        failed_sessions.append(session_id)

for session_id in failed_sessions:
    await self.remove_connection(session_id)

教训:Python 字典遍历时不能修改大小,必须先收集 keys 再删除!

3.3 连接清理机制

移除连接
python 复制代码
async def remove_connection(self, session_id: str):
    """移除连接并清理所有映射
    
    Args:
        session_id: 要移除的会话 ID
    """
    
    # ✅ 关键:获取 session 对象
    session = self._sessions.get(session_id)
    if not session:
        return
    
    logger.info(f"移除连接:session_id={session_id[:16]}...")
    
    # ⚠️ 注意:必须清理四层映射
    
    # 第 1 层:删除 session
    self._sessions.pop(session_id, None)
    
    # 第 2 层:从 client_sessions 中删除
    client_sessions = self._client_sessions.get(session.client_id, set())
    client_sessions.discard(session_id)
    if not client_sessions:
        self._client_sessions.pop(session.client_id, None)
    
    # 第 3 层:从 device_sessions 中删除
    device_sessions = self._device_sessions.get(session.device_fingerprint, set())
    device_sessions.discard(session_id)
    if not device_sessions:
        self._device_sessions.pop(session.device_fingerprint, None)
    
    # 第 4 层:从 connection_map 中删除
    conn_id = id(session.connection)
    self._connection_map.pop(conn_id, None)
    
    # 关闭 WebSocket 连接
    try:
        await session.connection.close()
    except Exception:
        pass  # 忽略关闭异常

最佳实践

  1. 使用 discard() 而非 remove(),避免 KeyError
  2. 清理后检查集合是否为空,空集合要从字典删除(防止内存泄漏)
  3. 关闭 WebSocket 连接时要捕获异常(可能已经断开)

四、问题诊断与修复 ------ 从"消息群发"到精准路由

4.1 问题现象:私密消息被群发

用户报告

"我正在和 AI 讨论工作事项,结果我的同事说他在他的浏览器上看到了我的对话内容!"

服务器日志

复制代码
2026-03-14 09:15:23 | bridge | INFO | 收到消息:user_id=user_001, session_id=sess_abc
2026-03-14 09:15:25 | bridge | INFO | LLM 响应生成完毕
2026-03-14 09:15:25 | bridge | INFO | 广播消息:发送给所有连接(5 个)
2026-03-14 09:15:25 | bridge | WARNING | 用户 user_002 投诉:收到他人私密消息

奇怪:明明是私密对话,为什么会广播给所有人?

4.2 根因分析:广播模式滥用

排查步骤

1️⃣ 检查发送逻辑

python 复制代码
# 查看代码
async def handle_llm_response(response, session_id):
    # ❌ 错误:使用了 broadcast_to_all()
    await conn_manager.broadcast_to_all(response)  # 广播给所有连接!

2️⃣ 发现问题

复制代码
原始设计:
- 为了"测试方便",使用了广播模式
- 认为"所有连接都是同一用户的"(错误假设)
- 没有考虑多用户共享服务器的场景

3️⃣ 根本原因混淆了单播和广播的使用场景

4.3 修复方案:基于路由键的精准发送

修复 1:改用定点发送

python 复制代码
# ✅ 修改后
async def handle_llm_response(response, session_id):
    # 使用 session_id 精准发送
    await conn_manager.send_to_session(session_id, response)

修复 2:增加路由键验证

python 复制代码
# ✅ 新增:路由键类型检查
class RoutingKey(str, Enum):
    SESSION = "session"      # 单播
    CLIENT = "client"        # 组播
    DEVICE = "device"        # 按设备
    BROADCAST = "broadcast"  # 广播

async def send_message(
    self,
    routing_key_type: RoutingKey,
    target_id: str,
    message: dict
):
    # 根据路由键类型选择发送策略
    if routing_key_type == RoutingKey.SESSION:
        await self.send_to_session(target_id, message)
    elif routing_key_type == RoutingKey.CLIENT:
        await self.send_to_client(target_id, message)
    # ... 其他类型

验证结果

复制代码
✅ 步骤 1:用户 A 发送消息,session_id=sess_abc
✅ 步骤 2:LLM 响应只发送给 sess_abc
✅ 步骤 3:用户 B 不会收到用户 A 的消息

4.4 经验教训:学到了什么?

Checklist

  • 严格区分单播、组播、广播三种模式
  • 默认使用单播,除非明确需要群发
  • 广播模式必须有权限控制(仅管理员可用)
  • 日志必须记录发送目标和实际接收者

避坑指南

  1. 不要假设所有连接都是同一用户:服务器通常服务多个用户
  2. 测试环境和生产环境隔离:测试用的广播代码不能带到生产
  3. 代码审查重点关注消息发送逻辑:这是最容易出隐私问题的地方

五、性能优化与最佳实践

5.1 性能瓶颈分析

Profiling 数据

复制代码
add_connection():      0.03ms  (四层字典插入)
send_to_session():     0.05ms  (字典查找 + 网络发送)
send_to_client():      0.12ms  (平均,取决于会话数量)
remove_connection():   0.04ms  (四层字典删除)

结论:网络 IO 是主要耗时,内存操作极快(微秒级)。

5.2 优化策略

策略 1:弱引用防止内存泄漏
python 复制代码
# ✅ 使用 weakref 管理连接对象
import weakref

class BridgeConnectionManager:
    def __init__(self):
        # 使用 WeakValueDictionary
        self._sessions: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
    
    def add_connection(self, session_id: str, session: ClientSession):
        # 弱引用:当 session 对象被垃圾回收时,字典项自动消失
        self._sessions[session_id] = session

代价 :增加弱引用管理开销
收益:彻底防止内存泄漏

策略 2:批量发送优化
python 复制代码
# ✅ 针对组播的批量优化
async def send_to_client_batch(
    self,
    client_id: str,
    messages: List[dict]
) -> int:
    """批量发送多条消息给同一用户"""
    session_ids = self._client_sessions.get(client_id)
    if not session_ids:
        return 0
    
    success_count = 0
    for session_id in session_ids:
        for message in messages:
            if await self.send_to_session(session_id, message):
                success_count += 1
    
    return success_count

代价 :增加调用复杂度
收益:减少网络 RTT,提升吞吐量

5.3 最佳实践总结

Do's(推荐做法):

  • ✅ 使用四层映射支持多维度查询
  • ✅ 默认使用单播,谨慎使用广播
  • ✅ 实现连接丢失自动清理机制
  • ✅ 为每种发送模式编写单元测试
  • ✅ 日志记录发送目标(脱敏处理)

Don'ts(避免做法):

  • ❌ 在生产环境使用广播模式发送私密消息
  • ❌ 遍历字典时直接删除项
  • ❌ 忽略连接异常(必须 try-except)
  • ❌ 不设置连接超时清理(会内存泄漏)
  • ❌ 混用不同的路由键类型

黄金法则

消息路由的第一原则是精准:能单播绝不组播,能组播绝不广播。


六、总结与展望

6.1 核心要点回顾

本文讲解了 WebSocket 路由机制的完整实现:

3 个关键点

  1. 四层映射设计:session_id→client_id→device_fingerprint→connection,支持多维度查询
  2. 三种发送模式:单播(精准)、组播(灵活)、广播(谨慎使用)
  3. 自动清理机制:连接断开时自动清理四层映射,防止内存泄漏

1 个核心公式

复制代码
WebSocket 路由 = BridgeConnectionManager(四层映射) + 路由键策略 (单播/组播/广播) + 自动清理机制

6.2 下一步学习方向

前置知识

  • ✅ WebSocket 协议基础(RFC 6455)
  • ✅ Python 字典和集合数据结构
  • ✅ 异步编程(async/await)
  • ✅ 弱引用和内存管理

后续主题

  • 📖 下一篇:《第 05 篇:心跳与重连机制------指数退避算法在 WebSocket 中的实践》
  • 🔜 下下一篇:《第 06 篇:离线消息队列设计------异步任务队列在实时通信中的应用》

扩展阅读

6.3 互动环节

思考题

  1. 如果你的应用场景需要支持百万级并发连接,应该如何改造 BridgeConnectionManager?
  2. 如何实现跨服务器的 WebSocket 路由(分布式场景)?

讨论话题

在你的项目中,遇到过哪些消息路由的挑战?你是如何实现精准投递的?欢迎在评论区分享你的经验!


下期预告:《第 05 篇:心跳与重连机制》

  • 💓 固定间隔重连 vs 指数退避 + 随机抖动
  • ⏱️ 心跳超时检测的双重定时器设计
  • 🔄 重连次数限制与永久放弃的权衡
  • 🤝 客户端与服务端的心跳协同优化

敬请期待!


附录 A:完整代码清单

文件路径 行数 作用
winclaw_server/remote_server/core/connection_manager.py 320 行 BridgeConnectionManager 核心实现
winclaw_server/remote_server/api/websocket.py 95 行 WebSocket 处理器
winclaw_server/remote_server/utils/routing.py 65 行 路由键定义与验证
tests/test_websocket_routing.py 180 行 路由测试用例

总代码量 :约 660 行
关键方法 :11 个(add_connection、send_to_session、send_to_client、remove_connection 等)
测试用例:26 个(覆盖单播、组播、广播、异常处理等场景)


附录 B:参考资料

  1. FastAPI WebSockets Documentation
  2. RFC 6455 - The WebSocket Protocol
  3. Python weakref Module
  4. Design Patterns for Message Routing
  5. 上一篇:《第 03 篇:请求路由机制》
  6. 下一篇:《第 05 篇:心跳与重连机制》(待发布)

版权声明:本文为 CSDN 博主「翁勇刚」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接https://blog.csdn.net/yweng18/article/details/xxxxxx(待发布后更新)

相关推荐
瘾大侠2 小时前
HTB - VariaType
网络·安全·web安全·网络安全
安科士andxe2 小时前
100G 长距光传输场景选型指南:安科士 QSFP28 ZR4 如何适配全场景需求
网络
Storynone2 小时前
【Day】LeetCode:134. 加油站,135. 分发糖果,860. 柠檬水找零,406. 根据身高重建队列
python·算法·leetcode
信看2 小时前
亚马逊 AWS MQTT(S) 测试
网络·云计算·aws
阿_旭2 小时前
基于YOLO26深度学习的茶叶病害智能检测识别系统【python源码+Pyqt5界面+数据集+训练代码】
人工智能·python·深度学习·茶叶病害检测
好家伙VCC2 小时前
**NumPy中的高效数值计算:从基础到进阶的实战指南**在现代数据科学与机器学习领域
java·python·机器学习·numpy
荷蒲2 小时前
【小白量化机器人】爬取财经新闻并利用本地大模型评分选择合适交易策略
人工智能·python·机器学习·ai·金融·本地大模型
&变形记¥2 小时前
openclaw升级/重启
python
ewboYang2 小时前
自学全栈搭建python [fastapi] + uniapp [vue3+ts]项目
python·uni-app·fastapi