犀牛鸟 2026 研究笔记 estelledc.github.io

第 6 章:多路径 QUIC——同时走两条路

从外卖配送说起

你点了外卖,配送平台发现有两条路可以到你家:

单路径模式:骑手只走一条路。走 A 赌运气,走 B 浪费 A 畅通时的优势。

多路径模式:两个骑手同时出发!

这就是多路径 QUIC 的核心价值:同时使用多条网络路径,提升带宽、降低延迟、增强可靠性


为什么多路径在移动时代至关重要?

今天的手机有多少条”路”?

你的手机此刻可用的网络接口:

┌─────────────────┐
│    你的手机       │
├─────────────────┤
│  WiFi (路径1)    │ → 路由器 → 宽带ISP → 互联网
│  4G/5G (路径2)   │ → 基站 → 运营商核心网 → 互联网
│  (未来:WiFi6E)  │ → 另一个频段的WiFi
└─────────────────┘

这两条路径的特征完全不同:
  WiFi:  低延迟(5-20ms),高带宽(100Mbps+),但信号范围小、易断
  4G:    高延迟(30-80ms),中带宽(10-50Mbps),但覆盖广、稳定
  5G:    低延迟(5-15ms),超高带宽,但覆盖不连续

单路径的痛点

场景1:视频通话时走出 WiFi 范围
  单路径(WiFi):画面卡住 → 断线重连(TCP) 或 切换重连(QUIC迁移)
                            → 即使QUIC连接迁移也有短暂中断
  多路径:WiFi 断了但 4G 一直在传 → 用户无感知切换

场景2:你想用满两条路径的带宽
  单路径:只能用WiFi(100Mbps)或4G(50Mbps)之一
  多路径:两条路径同时用 → 理论最大 150Mbps(带宽聚合)

场景3:弱网环境
  单路径:WiFi 信号弱,丢包 10%,体验很差
  多路径:WiFi 不稳时把重要数据冗余发到 4G → 冗余提高可靠性

Multipath QUIC vs 普通 QUIC 连接迁移

等等,第 3 章说过 QUIC 已经支持连接迁移了。那多路径和连接迁移有什么区别?

连接迁移(Connection Migration):
  一次只用一条路径,切换时从旧路径"搬"到新路径
  
  时间轴:
  ─────[WiFi]────────│切换│────────[4G]─────────
                      ↑ 短暂中断

多路径(Multipath):
  同时使用多条路径,数据可以分散在多条路径上
  
  时间轴:
  ─────[WiFi]────────────────[WiFi衰减]────────
  ─────[4G]──────────────────[4G继续]──────────
       ↑ 同时在用!            ↑ 无缝!

关键区别:
  连接迁移 = 换路(旧的不用了)
  多路径   = 开两条路同时用
特性 连接迁移 多路径
同时活跃路径数 1 2+
带宽聚合 不支持 支持
切换时中断 有短暂中断 无中断
冗余传输 不支持 支持
复杂度
标准化状态 RFC 9000 (已完成) draft-ietf-quic-multipath (进行中)

多路径 QUIC 的技术架构

核心概念

┌───────────────────────────────────────────────────────┐
│                  QUIC Connection                       │
│                                                       │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐               │
│  │ Stream 0│ │ Stream 4│ │ Stream 8│  ← 应用层流    │
│  └────┬────┘ └────┬────┘ └────┬────┘               │
│       │           │           │                      │
│       └───────────┼───────────┘                      │
│                   │                                   │
│            ┌──────┴──────┐                           │
│            │  Scheduler  │  ← 调度器:决定数据走哪条路  │
│            └──────┬──────┘                           │
│           ┌───────┴───────┐                          │
│     ┌─────┴─────┐  ┌─────┴─────┐                    │
│     │  Path 0   │  │  Path 1   │   ← 网络路径      │
│     │  (WiFi)   │  │  (4G)     │                    │
│     │ RTT:20ms  │  │ RTT:80ms  │                    │
│     │ CC:BBR3   │  │ CC:BBR3   │   ← 每路径独立CC  │
│     │ Recovery  │  │ Recovery  │   ← 每路径独立恢复  │
│     └───────────┘  └───────────┘                    │
│                                                       │
└───────────────────────────────────────────────────────┘

每个组件的职责

Path(路径):
  - 一对本地地址和远端地址
  - 有自己的 Connection ID
  - 有自己的拥塞控制器(独立的 CWND)
  - 有自己的 RTT 估算
  - 有自己的丢包恢复机制

Scheduler(调度器):
  - 接收上层要发送的数据
  - 决定这个数据包走哪条路径
  - 策略可以是:选最快的路径、两条都发、轮流发

Recovery(恢复):
  - 每条路径独立检测丢包
  - 路径 A 丢包不影响路径 B 的拥塞窗口
  - 重传可以走原路径也可以走另一条路径

三种调度策略:TQUIC 的核心设计

TQUIC 实现了三种调度策略,每种适合不同的场景:

策略一:MinRtt(最小延迟优先)

逻辑:每次发包时,选择 RTT 最小的路径

┌─────────────────────────────────────┐
│         MinRtt Scheduler            │
│                                     │
│  输入:要发送的数据帧                 │
│                                     │
│  决策过程:                           │
│  1. 查看所有活跃路径的当前 RTT        │
│     Path 0 (WiFi): RTT = 15ms       │
│     Path 1 (4G):   RTT = 60ms       │
│  2. 选择 RTT 最小的路径              │
│     → 选择 Path 0                   │
│  3. 检查该路径的拥塞窗口是否有空间    │
│     有空间 → 在 Path 0 上发送        │
│     没空间 → 选择次优路径(Path 1)    │
└─────────────────────────────────────┘

适用场景:对延迟敏感的应用(视频通话、游戏)
优点:数据总是走最快的路,端到端延迟最低
缺点:慢路径可能很少被使用 → 带宽聚合效果差
// TQUIC 中 MinRtt 调度器的核心逻辑(简化)
impl MultipathScheduler for MinRttScheduler {
    fn select_path(&self, paths: &PathMap) -> Option<PathId> {
        paths.iter()
            .filter(|p| p.is_active() && p.can_send())  // ← 过滤可用路径
            .min_by_key(|p| p.smoothed_rtt())           // ← 选RTT最小的
            .map(|p| p.id())
    }
}

策略二:Redundant(冗余发送)

逻辑:每个数据帧同时在所有活跃路径上发送一份

┌─────────────────────────────────────┐
│       Redundant Scheduler           │
│                                     │
│  输入:要发送的数据帧 F              │
│                                     │
│  决策过程:                           │
│  1. 在 Path 0 上发送 F 的副本1       │
│  2. 在 Path 1 上发送 F 的副本2       │
│                                     │
│  接收端:                            │
│  先到的副本被接受,后到的丢弃         │
│                                     │
│  结果:延迟 = min(Path0延迟, Path1延迟)│
└─────────────────────────────────────┘

适用场景:对可靠性和延迟都极度敏感的场景
  - 金融交易(毫秒级延迟差异=钱)
  - 远程手术(不能有任何中断)
  - 关键控制信号
  
优点:
  - 最低延迟(等于最快路径的延迟)
  - 最高可靠性(一条路径完全断了也不影响)
  
缺点:
  - 带宽浪费(所有数据发两份)
  - 总带宽 = min(路径带宽),不能聚合
  - 两条路径的流量都在增加
// TQUIC 中 Redundant 调度器(简化)
impl MultipathScheduler for RedundantScheduler {
    fn select_paths(&self, paths: &PathMap) -> Vec<PathId> {
        // 选择所有可用路径——数据会在每条路径上各发一份
        paths.iter()
            .filter(|p| p.is_active() && p.can_send())
            .map(|p| p.id())
            .collect()
    }
}

策略三:RoundRobin(轮询)

逻辑:按顺序轮流在各路径上发送

┌─────────────────────────────────────┐
│       RoundRobin Scheduler          │
│                                     │
│  输入:连续的数据帧 F1, F2, F3, F4   │
│                                     │
│  决策过程:                           │
│  F1 → Path 0                        │
│  F2 → Path 1                        │
│  F3 → Path 0                        │
│  F4 → Path 1                        │
│  ...                                │
└─────────────────────────────────────┘

适用场景:两条路径质量相近时,追求带宽聚合
优点:两条路径的带宽都被利用
缺点:
  - 如果路径质量差异大,会引入严重的重排序
    (快路径的包先到,慢路径的后到 → 应用层乱序)
  - 需要在接收端做重排序缓冲

三种策略对比

                    延迟        带宽利用      可靠性     带宽浪费
MinRtt              最优        中等          中等       无
Redundant           最优        最差(不聚合)  最优       2倍
RoundRobin          中等        最优(聚合)    中等       无

选择指南:
  ┌─────────────────────────────────────────┐
  │ 你最在意什么?                            │
  │                                         │
  │ 延迟最低 + 省带宽 ──→ MinRtt            │
  │ 绝对不能断 ─────────→ Redundant         │
  │ 下载速度最快 ───────→ RoundRobin        │
  │ 不确定 ─────────────→ MinRtt (默认推荐)  │
  └─────────────────────────────────────────┘

多路径 QUIC 的核心挑战

多路径听起来很美好,但实现起来有很多难题:

挑战一:接收端重排序

发送顺序:帧1, 帧2, 帧3, 帧4, 帧5

调度结果:
  Path 0 (RTT=20ms): 帧1, 帧3, 帧5
  Path 1 (RTT=80ms): 帧2, 帧4

接收端收到顺序:
  t=20ms:  帧1 到达 (Path 0)
  t=20ms:  帧3 到达 (Path 0)
  t=20ms:  帧5 到达 (Path 0)
  t=80ms:  帧2 到达 (Path 1)  ← 比帧3晚到!
  t=80ms:  帧4 到达 (Path 1)

问题:
  t=20ms 时,帧1和帧3到了但帧2没到
  应用层能看到帧1的数据,但帧2的数据要等到 t=80ms
  如果是同一个 Stream 内的数据 → 流内队头阻塞!

解决方案:
  1. 接收端维护重排序缓冲区
  2. MinRtt 策略尽量避免这个问题(数据主要走快路径)
  3. 调度器可以把同一个 Stream 的数据尽量放在同一条路径

挑战二:何时声明丢包?

单路径丢包检测:
  包 100 之后的 3 个包(101,102,103)被确认 → 包100丢了
  简单明确

多路径丢包检测:
  路径 A 上发了包 100(包号是全局的还是每路径的?)
  路径 B 上发了包 101-103 并被确认
  
  问题:路径 B 的确认能用来判定路径 A 的包丢失吗?
  
  如果路径 B 的 RTT 是路径 A 的 4 倍:
    路径 B 确认包 101-103 时,路径 A 的包 100 可能还在路上
    → 不能用跨路径的确认来判定丢包!

TQUIC 的做法:
  每条路径独立做丢包检测
  路径 A 只看路径 A 上发出的包的确认状态
  路径 A 丢包 → 只影响路径 A 的拥塞窗口

挑战三:路径质量突变

场景:你走进电梯
  WiFi 路径:信号突然从满格变成零
  4G 路径:还有微弱信号

需要快速做出决策:
  1. WiFi 完全不可用了 → 停止在 WiFi 上发送
  2. 把所有流量转移到 4G
  3. 出电梯后 WiFi 恢复 → 重新激活 WiFi 路径
  
  关键指标:切换的速度
  太慢:WiFi 上积压大量数据,超时重传,用户感觉卡
  太快(误判):WiFi 只是短暂波动就放弃,浪费了好路径

TQUIC 通过 PathMap 管理路径状态:
  每条路径有 Active / Degraded / Unavailable 状态
  基于连续丢包次数和 RTT 飙升来判断路径质量

挑战四:队头阻塞的”回归”

多路径虽然是为了解决问题,但也可能引入新的队头阻塞:

Stream 级别的阻塞(多路径特有):
  Stream A 的帧 1-3 被调度到:
    帧1 → Path 0 (快, 到达)
    帧2 → Path 1 (慢, 未到达)  ← 卡在这里
    帧3 → Path 0 (快, 到达)
    
  即使帧1和帧3都到了,Stream A 还是要等帧2
  因为 Stream 内部必须有序交付
  
  这比单路径更严重:
  单路径:帧2丢了需要重传(同路径RTT时间)
  多路径:帧2可能只是在慢路径上"还在路上"(慢路径RTT时间)

缓解策略:
  1. 同一 Stream 的帧尽量调度到同一路径
  2. MinRtt 策略本身就减少了这个问题
  3. 接收端设置合理的重排序超时

IETF 多路径 QUIC 标准化进展

draft-ietf-quic-multipath

多路径 QUIC 的标准化工作在 IETF 的 QUIC 工作组进行中:

标准化时间线:
  2021: 提出初始草案
  2022: 合并多个提案为统一草案
  2023: 第 10+ 版草案,核心设计趋于稳定
  2024-2025: 持续修订,争取进入 RFC 发布流程
  
  当前状态(2025):仍是 Internet-Draft,尚未成为 RFC

标准草案的核心设计决策

决策1:包号空间
  选项A:全局包号空间(所有路径共享)
    优点:简单
    缺点:跨路径确认复杂
    
  选项B:每路径独立包号空间  ← 草案选择了这个
    优点:路径间完全隔离,丢包检测简单
    缺点:需要额外机制关联同一连接的不同路径

决策2:路径标识
  使用 Path ID(小整数)标识每条路径
  不同路径使用不同的 Connection ID
  
决策3:新帧类型
  PATH_ABANDON:放弃一条路径
  PATH_STATUS:通告路径偏好(主用/备用/只接收)
  ACK_MP:多路径 ACK(指定是哪条路径的确认)

TQUIC 的多路径实现深入

TQUIC 是目前唯一原生实现了多路径 QUIC 的开源项目。让我们看看它的关键数据结构:

PathMap:路径管理

// TQUIC 中的路径管理(简化)
pub struct PathMap {
    paths: Vec<Path>,
    active_path_id: PathId,
    
    // 每条路径的信息
}

pub struct Path {
    id: PathId,
    local_addr: SocketAddr,
    peer_addr: SocketAddr,
    
    // 连接 ID
    dcid: ConnectionId,
    scid: ConnectionId,
    
    // 路径状态
    state: PathState,
    
    // 独立的拥塞控制
    congestion_controller: Box<dyn CongestionController>,
    
    // 独立的 RTT 估算
    rtt: RttEstimator,
    
    // 独立的丢包恢复
    recovery: Recovery,
    
    // 路径验证状态
    validated: bool,
    challenge_data: Option<[u8; 8]>,
}

pub enum PathState {
    Active,     // 正常使用
    Degraded,   // 质量下降(高丢包/高RTT)
    Standby,    // 备用(不主动发数据)
    Closed,     // 已关闭
}

MultipathScheduler trait:调度器接口

// TQUIC 的调度器 trait(简化)
pub trait MultipathScheduler: Send + Sync {
    /// 选择要发送数据的路径
    fn on_select_path(
        &mut self,
        paths: &PathMap,
        frame: &Frame,
    ) -> Option<PathId>;
    
    /// 路径状态变化时的回调
    fn on_path_updated(
        &mut self, 
        path_id: PathId, 
        event: PathEvent,
    );
    
    /// 获取调度器名称(用于日志)
    fn name(&self) -> &str;
}

// 使用示例:注册自定义调度器
let mut config = Config::new()?;
config.set_multipath_scheduler(Box::new(MyCustomScheduler::new()));

这个 trait 设计是 TQUIC 的核心扩展点——犀牛鸟参赛者可以实现自定义的调度策略。

每路径 Recovery 设计

// TQUIC 中每路径独立的丢包恢复(简化)
impl Path {
    pub fn on_ack_received(&mut self, ack: &AckFrame) {
        // 1. 只处理本路径上发出的包的确认
        let newly_acked = self.recovery.on_ack(ack);
        
        // 2. 更新本路径的 RTT
        if let Some(rtt_sample) = newly_acked.rtt_sample {
            self.rtt.update(rtt_sample);
        }
        
        // 3. 通知本路径的拥塞控制器
        self.congestion_controller.on_ack(
            newly_acked.bytes,
            self.rtt.smoothed(),
        );
        
        // 4. 检测本路径的丢包
        let lost_packets = self.recovery.detect_loss(self.rtt.smoothed());
        if !lost_packets.is_empty() {
            self.congestion_controller.on_loss();
        }
    }
}

多路径的实际效果:性能数据

带宽聚合效果

测试环境:
  Path 0: 50Mbps, 20ms RTT, 0% 丢包
  Path 1: 30Mbps, 60ms RTT, 0% 丢包

理论最大聚合带宽:80Mbps

实际测试结果(大文件下载):
  RoundRobin: ~72Mbps (90% 效率)
  MinRtt:     ~55Mbps (69% 效率,大部分走 Path 0)
  Redundant:  ~45Mbps (56%,受限于去重)
  单路径:     ~50Mbps (只用 Path 0)

  为什么 RoundRobin 达不到 100%?
  → Path 1 的高 RTT 导致重排序,接收端等待时间增加
  → 两路径 RTT 差异越大,聚合效率越低

切换可靠性效果

测试场景:Path 0 在 t=5s 时突然断开

  单路径 QUIC(连接迁移):
    t=5.0s: 检测到路径不可用
    t=5.0s-5.3s: 路径验证(PATH_CHALLENGE/RESPONSE)
    t=5.3s: 迁移完成,恢复传输
    中断时间: ~300ms
    
  多路径 QUIC (MinRtt):
    t=5.0s: Path 0 不可用
    t=5.0s: Path 1 已经在工作,立即承担所有流量
    中断时间: ~0ms(但带宽下降)
    
  优势:对用户完全无感知的切换

多路径 QUIC 与 MPTCP 的对比

在 QUIC 之前,TCP 层面也有多路径方案:Multipath TCP (MPTCP, RFC 8684)。

┌──────────────────┬────────────────────┬───────────────────────┐
│                  │     MPTCP          │   Multipath QUIC      │
├──────────────────┼────────────────────┼───────────────────────┤
│ 标准化           │ RFC 8684 (2020)    │ Draft (进行中)         │
│ 部署情况         │ iOS/macOS 默认启用  │ TQUIC 等少数实现       │
│ 中间设备兼容性   │ 差(TCP头被篡改)   │ 好(QUIC加密保护)     │
│ 加密保护         │ 无(TCP明文头部)   │ 有(QUIC全程加密)     │
│ 每路径独立CC     │ 是                 │ 是                    │
│ 实现位置         │ 内核态             │ 用户态                 │
│ 升级部署         │ 需改OS内核         │ 改应用即可             │
│ 已知问题         │ 中间设备干扰       │ 标准化未完成           │
└──────────────────┴────────────────────┴───────────────────────┘

MPTCP 的最大问题是中间设备兼容性

MPTCP 部署障碍:
  1. 很多防火墙看到不认识的 TCP Option 就丢包
  2. NAT 设备可能修改 TCP 头部,破坏 MPTCP 签名
  3. 某些运营商的透明代理会重写 TCP 头部
  
  结果:即使两端都支持 MPTCP,中间路径不一定允许
  苹果的实践:iOS 用 MPTCP 时,如果检测到不兼容就回退到普通 TCP

Multipath QUIC 没有这个问题:
  QUIC 头部加密 → 中间设备无法读取或篡改
  对中间设备来说,QUIC 就是普通的 UDP 包
  只要 UDP 能通,多路径就能工作

犀牛鸟可以贡献什么?

多路径 QUIC 是 TQUIC 的核心方向,也是犀牛鸟参赛的主要切入点:

贡献方向一:新的调度策略

现有策略的局限:
  MinRtt: 不利用慢路径的带宽
  RoundRobin: 不考虑路径质量差异
  Redundant: 浪费带宽

可以研究的新策略:

1. 加权调度(Weighted Scheduler):
   按路径带宽比例分配数据
   WiFi 50Mbps, 4G 30Mbps → WiFi分配 62.5%, 4G 分配 37.5%

2. 自适应调度(Adaptive Scheduler):
   实时监测路径质量,动态调整策略
   正常时用 MinRtt → 检测到快路径拥塞 → 自动切到加权

3. 流感知调度(Stream-aware Scheduler):
   实时流(视频) → MinRtt(低延迟路径)
   大文件流(下载) → RoundRobin(聚合带宽)
   控制流(信令) → Redundant(保证可靠)

多路径 QUIC 的技术挑战

挑战一:乱序重组

问题:两条路径延迟不同,数据到达顺序可能乱

发送顺序:Packet 1(WiFi), Packet 2(4G), Packet 3(WiFi), Packet 4(4G)

到达顺序(WiFi快,4G慢):
  Packet 1 (WiFi, 20ms后到达)
  Packet 3 (WiFi, 25ms后到达)
  Packet 2 (4G,  80ms后到达)  ← 比 Packet 3 还晚!
  Packet 4 (4G,  85ms后到达)

接收端需要:
  1. 足够大的重排序缓冲区
  2. 不要把"延迟到达"误判为"丢包"
  3. 正确地向应用层按序交付

TQUIC 的解决方案:
  - 每路径独立的包号空间(避免全局乱序)
  - 路径感知的丢包检测阈值
  - 自适应的重排序窗口

挑战二:路径之间的耦合

问题:一条路径的拥塞不应该影响另一条

场景:
  WiFi: 正常,CWND = 100KB
  4G:  突然丢包严重

错误做法:整个连接的 CWND 减半
  → WiFi 也被降速了!

正确做法(TQUIC):
  每条路径独立的拥塞控制器
  4G 路径减速,WiFi 路径不受影响

但也不能完全独立——调度器需要知道各路径状态
  → 调度器和拥塞控制器之间需要信息共享

挑战三:缓冲区膨胀(Buffer Bloat)

问题:快路径的数据先到,慢路径的数据还在路上
       接收端需要缓存快路径的数据等慢路径

示意:
  WiFi数据:  [1][3][5][7][9]  ← 很快到达
  4G数据:    [2][4][6][8][10] ← 慢慢到达

  接收端缓冲区:
  时刻1: [1] 可交付, [3] 缓存等待 [2]
  时刻2: [1] 可交付, [3][5] 缓存等待 [2]
  时刻3: [1] 可交付, [3][5][7] 缓存等待 [2]
  ...缓冲区越来越大!

解决思路:
  1. 调度器尽量让两条路径的数据"同时到达"
     → MinRtt 策略考虑路径延迟差异
  2. 接收端流控限制缓冲区大小
  3. 极端情况下放弃慢路径

挑战四:队头阻塞的回归

讽刺的问题:多路径可能重新引入队头阻塞

单路径 QUIC:
  Stream A 和 Stream B 互不影响 ← QUIC 的核心优势

多路径 QUIC(如果调度不当):
  Stream A 的数据分散在两条路径上
  快路径的帧先到,但要等慢路径的帧
  → 流内队头阻塞又回来了!

解决思路:
  1. 尽量让同一个流的数据走同一条路径
  2. 或者接受一定的乱序,用更大的缓冲区
  3. 对延迟敏感的流只走最快路径(流感知调度)

IETF 多路径 QUIC 标准化进展

draft-ietf-quic-multipath 核心设计

IETF 多路径 QUIC 草案的关键帧类型:

PATH_NEW Frame:
  - 通知对端"我要使用一条新路径了"
  - 包含路径标识符

PATH_ABANDON Frame:
  - 通知对端"我不再使用这条路径了"
  - 触发路径资源释放

ACK_MP Frame:
  - 多路径 ACK,指明确认的是哪条路径的包
  - 每条路径有独立的包号空间

关键设计决策:
  - 每条路径独立的包号空间(vs 共享包号空间)
  - 路径标识用 Connection ID 映射
  - 拥塞控制 per-path
  - 调度策略不在标准中规定(留给实现)

标准化时间线

2017: 最早的多路径 QUIC 研究论文
2021: draft-ietf-quic-multipath-00 发布
2022-2024: 活跃讨论和修改(已到 draft-10+)
2025: 接近完成,但仍有争议点
预期: 2025-2026 可能成为 RFC

争议点:
  1. 是否允许单包跨路径(当前:不允许)
  2. 路径 ID 的分配方式
  3. 与现有单路径实现的向后兼容性

多路径在实际产品中的应用

腾讯的实际案例

微信视频通话:
  - WiFi + 4G 同时使用
  - WiFi 正常时优先走 WiFi(低延迟)
  - WiFi 卡顿时无缝切换到 4G
  - 用户完全无感知

QQ 视频:
  - 类似策略
  - 额外的"冗余模式":重要帧在两条路径上都发一份
  - 牺牲带宽换取可靠性

腾讯云 CDN:
  - 服务端多路径加速
  - 聚合多条路径的带宽

Apple 的 MPTCP 经验(作为参考)

iOS 从 iOS 7 开始支持 Multipath TCP:
  - Siri 使用 MPTCP(WiFi + Cellular)
  - Apple Music 流媒体
  - Apple Maps 导航

经验教训(QUIC 多路径可以借鉴):
  1. 电量消耗是关键问题(两个无线接口同时开启)
  2. 用户可能不愿意同时用WiFi和流量(流量费用)
  3. 需要智能的路径选择:不是总是两条都用
  4. 策略应该是"按需使用第二条路径"

代码视角:实现一个简单的调度器

如果你要为 TQUIC 贡献一个新的调度策略,框架大概是这样:

// 定义你的调度器
pub struct WeightedScheduler {
    weights: HashMap<PathId, f64>,  // 每条路径的权重
}

impl MultipathScheduler for WeightedScheduler {
    fn on_path_updated(&mut self, path_id: PathId, stats: &PathStats) {
        // 根据路径带宽更新权重
        let total_bw: f64 = self.paths.values()
            .map(|p| p.bandwidth)
            .sum();
        self.weights.insert(
            path_id,
            stats.bandwidth / total_bw,  // ← 按带宽比例分配
        );
    }
    
    fn select_path(&self, paths: &[PathId]) -> Option<PathId> {
        // 选择"欠债最多"的路径(保证长期比例正确)
        paths.iter()
            .max_by(|a, b| {
                let debt_a = self.weights[a] - self.actual_ratio[a];
                let debt_b = self.weights[b] - self.actual_ratio[b];
                debt_a.partial_cmp(&debt_b).unwrap()
            })
            .copied()
    }
}

这个 Weighted Scheduler 的思路:

  1. 根据每条路径的带宽计算”应该分配的比例”
  2. 跟踪”实际分配的比例”
  3. 每次选择”最欠债”的路径(实际比例低于目标比例的)

本章小结

概念 关键点
多路径动机 带宽聚合 + 无缝切换 + 冗余可靠
TQUIC PathMap 每条路径独立管理,含独立拥塞控制
MinRtt 策略 选延迟最小的路径,适合实时应用
Redundant 策略 所有路径都发一份,适合关键数据
RoundRobin 策略 轮流发送,适合带宽聚合
核心挑战 乱序重组、路径耦合、缓冲区膨胀、HOL回归
IETF 进展 draft-ietf-quic-multipath,接近完成
唯一实现 TQUIC 是六大项目中唯一原生支持多路径的

一句话总结:多路径 QUIC 让一个连接同时使用多条网络路径,核心难点在于”如何聪明地调度数据到不同路径”。TQUIC 的 MultipathScheduler trait 提供了可插拔的调度框架,是犀牛鸟贡献的核心方向。


读完本章你能做什么

  1. 解释多路径 QUIC 的三个核心价值(带宽聚合、无缝切换、冗余)
  2. 说出 TQUIC 的三种调度策略及各自适用场景
  3. 描述 MultipathScheduler trait 的核心接口
  4. 列出多路径 QUIC 的四个技术挑战
  5. 设计一个简单的加权调度策略的伪代码
  6. 解释为什么 TQUIC 需要每路径独立的拥塞控制

常见误区

误区一:多路径就是”同时用两条路,速度翻倍”

正确理解:带宽聚合的实际效果通常远低于理论值。原因包括:1. 两条路径延迟不同导致乱序和重传;2. 调度算法的开销;3. 缓冲区限制。实际中 WiFi(50Mbps) + 4G(30Mbps) 可能只能达到 60-70Mbps 的聚合效果,而不是 80Mbps。

误区二:所有 QUIC 实现都支持多路径

正确理解:在我们研究的六大实现中,只有 TQUIC 原生支持多路径 QUIC。其他实现目前都只支持单路径(虽然支持连接迁移——但那是”从一条路径切换到另一条”,不是”同时使用多条”)。

误区三:多路径 QUIC 和连接迁移是一回事

正确理解:连接迁移是”放弃旧路径,切换到新路径”——任何时刻只用一条路径。多路径是”同时使用多条路径”——所有路径并行传输数据。连接迁移是 QUIC v1(RFC 9000)的标准功能,多路径是尚在标准化中的扩展。


导读目录:README.md 上一章:第 5 章 · 拥塞控制——在黑暗中找到最佳车速 下一章:第 7 章 · 社区标杆:quinn 与 quiche——极简 vs 全能