AWS 用随机图理论干掉胖树网络:路由器砍掉 69%,吞吐提升 33%

2026-06-04 24 预计阅读时间:1 分钟
来源:infoq.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:17 分钟

数据中心网络架构十几年来几乎没变过——胖树(fat-tree)是默认选项。AWS 最近公开了一项已经落地的大改动:他们用基于准随机图理论的扁平网络架构替代了胖树,新架构叫 Resilient Network Graphs,已经成为大多数新建数据中心的默认方案。路由器数量直接砍掉 69%,吞吐量提升 33%,网络功耗降低 40%。这不是论文里的设想,是已经在跑的生产架构。

胖树的瓶颈在哪

胖树的核心思路是层级扩展:ToR 交换机连到汇聚层,汇聚层连到核心层,流量逐级向上再向下。好处是路径可预测,坏处也很明显——

  • 层级越多,路由器越多:每一层都是成倍增长,一个典型三级胖树要几千台路由器。
  • 核心层是单点瓶颈:所有跨机架流量都要过核心,核心一旦拥塞,整栋数据中心吞吐塌方。
  • 超分比难以压缩:胖树的带宽逐级收窄,要降低超分比就得堆更多核心路由器,成本和功耗一起涨。
  • 故障域大:核心层路由器宕机影响面是整栋 DC,收敛慢、影响大。

AWS 的规模让这些问题更尖锐:几十万台服务器、跨机架流量占比越来越高,胖树的层级开销已经不可接受。

ShuffleBox + 准随机图:扁平到极致

Resilient Network Graphs 的关键设计只有两步:

第一步:去掉中间层,ToR 直连 ToR。 机架顶部的交换机不再往上汇聚,而是直接和其他机架的 ToR 建立连接,形成一张扁平的 mesh。

第二步:连接模式不用手工规划,用准随机图理论生成。 不是每个 ToR 都连所有其他 ToR(那线缆数量会爆炸),而是按准随机图的度数约束,让每个 ToR 只连有限数量的邻居,但整体拓扑具备随机图的短路径和高容错特性。

物理实现靠 ShuffleBox——一种被动光学器件,把多路光纤信号做物理级混洗(shuffle),不需要电信号处理,不需要路由决策,功耗几乎为零。ShuffleBox 放在机架行之间,ToR 的光纤拉到 ShuffleBox,出来就连到了随机分配的远端 ToR。

整张网络变成一张低度数、高连通的随机图:

  • 任意两个 ToR 之间通常 2-3 跳就能到达,不需要核心层。
  • 单条链路或单个 ToR 宕机,流量沿随机图的冗余路径绕行,故障收敛快。
  • 路由器总数大幅减少——中间层全没了。

数字背后的逻辑

指标 变化 原因
路由器数量 -69% 汇聚层和核心层被 ShuffleBox + 直连替代
吞吐量 +33% ToR 间直连消除了核心层超分瓶颈
网络功耗 -40% ShuffleBox 是被动光学器件,路由器数量也少了

69% 的路由器削减是最大的冲击。胖树里路由器数量大致是 O(k³)(k 是端口数),而准随机图里路由器数量只跟 ToR 数量线性相关,度数是常数。量级差异直接体现在这个百分比上。

吞吐量提升 33% 的关键不是链路变快了,而是超分比降低了。胖树核心层的超分比通常是 3:1 甚至更高,ToR 直连后跨机架带宽不再被核心层收窄,有效吞吐自然上去。

功耗下降 40% 是两个因素叠加:路由器少了(每台路由器几百瓦),ShuffleBox 几乎不耗电(被动光学混洗,没有芯片)。

用 Python 模拟:胖树 vs 准随机图的路由器数量对比

下面的脚本生成两种拓扑的路由器数量对比,你可以直接跑起来看差距。度数参数可以调整,观察随机图在不同连通度下的路由器需求。

#!/usr/bin/env python3
"""
fat_tree_vs_random_graph.py
对比胖树和准随机图拓扑的路由器数量与平均跳数。
直接运行: python3 fat_tree_vs_random_graph.py
"""

import random
import math
from collections import deque

def fat_tree_routers(k: int) -> dict:
    """
    k-port 胖树: k 个核心路由器, k²/2 个汇聚路由器, k²/2 个边缘路由器。
    总路由器 = k + k²/2 + k²/2 = k + k²
    总机架数 = k²/2 (每个边缘路由器连一个机架)
    """
    core = k
    agg = k * k // 2
    edge = k * k // 2
    total_routers = core + agg + edge
    racks = edge
    return {
        "topology": "fat-tree",
        "k": k,
        "total_routers": total_routers,
        "racks": racks,
        "core": core,
        "aggregation": agg,
        "edge": edge,
    }

def random_graph_routers(num_tors: int, degree: int) -> dict:
    """
    准随机图: 只有 ToR 路由器, 每个 ToR 有 degree 条直连链路到其他 ToR。
    总路由器 = num_tors (没有汇聚层和核心层)
    """
    return {
        "topology": "resilient-random-graph",
        "total_routers": num_tors,
        "racks": num_tors,
        "degree": degree,
        "core": 0,
        "aggregation": 0,
        "edge": num_tors,
    }

def build_random_graph(num_nodes: int, degree: int) -> dict:
    """
    构建一张准随机图 (近似 regular random graph),
    返回邻接表和平均最短路径跳数。
    """
    if degree >= num_nodes:
        degree = num_nodes - 1
    if degree < 2 or num_nodes < 3:
        return {"adj": {}, "avg_hops": float("inf")}

    adj = {i: set() for i in range(num_nodes)}

    # 简单方法: 每个节点随机选 degree 个不同邻居
    for i in range(num_nodes):
        candidates = [j for j in range(num_nodes) if j != i and j not in adj[i]]
        need = degree - len(adj[i])
        if need > 0 and candidates:
            chosen = random.sample(candidates, min(need, len(candidates)))
            for j in chosen:
                adj[i].add(j)
                adj[j].add(i)

    # 计算平均最短路径 (BFS, 取样加速)
    sample_size = min(200, num_nodes)
    sampled = random.sample(range(num_nodes), sample_size)
    total_hops = 0
    pairs = 0
    for src in sampled:
        dist = {src: 0}
        q = deque([src])
        while q:
            u = q.popleft()
            for v in adj[u]:
                if v not in dist:
                    dist[v] = dist[u] + 1
                    q.append(v)
        for dst in dist:
            if dst != src:
                total_hops += dist[dst]
                pairs += 1
    avg_hops = total_hops / pairs if pairs > 0 else float("inf")

    return {"adj": adj, "avg_hops": round(avg_hops, 2)}

def compare(k_values: list[int], degrees: list[int]):
    """打印对比表"""
    print("=" * 72)
    print(f"{'k':>4} | {'胖树路由器':>10} | {'机架':>6} | "
          f"{'随机图路由器':>12} | {'度数':>4} | {'路由器削减':>10} | {'平均跳数':>8}")
    print("-" * 72)

    for k in k_values:
        ft = fat_tree_routers(k)
        for d in degrees:
            rg = random_graph_routers(ft["racks"], d)
            reduction = (1 - rg["total_routers"] / ft["total_routers"]) * 100
            graph = build_random_graph(ft["racks"], d)
            print(f"{k:>4} | {ft['total_routers']:>10} | {ft['racks']:>6} | "
                  f"{rg['total_routers']:>12} | {d:>4} | {reduction:>9.1f}% | "
                  f"{graph['avg_hops']:>8}")
    print("=" * 72)

if __name__ == "__main__":
    random.seed(42)
    # k=48 是典型大型胖树; 度数 4-8 是准随机图常见范围
    compare(k_values=[8, 16, 24, 48], degrees=[4, 6, 8])

运行结果示例:

========================================================================
   k |   胖树路由器 |   机架 |   随机图路由器 | 度数 |   路由器削减 | 平均跳数
------------------------------------------------------------------------
   8 |        72 |     32 |           32 |    4 |     55.6% |     2.85
   8 |        72 |     32 |           32 |    6 |     55.6% |     2.43
   8 |        72 |     32 |           32 |    8 |     55.6% |     2.18
  16 |       272 |    128 |          128 |    4 |     52.9% |     3.42
  16 |       272 |    128 |          128 |    6 |     52.9% |     2.93
  16 |       272 |    128 |          128 |    8 |     52.9% |     2.61
  24 |       600 |    288 |          288 |    4 |     52.0% |     3.71
  24 |       600 |    288 |          288 |    6 |     52.0% |     3.15
  24 |       600 |    288 |          288 |    8 |     52.0% |     2.80
  48 |      2352 |   1152 |         1152 |    4 |     51.3% |     4.12
  48 |      2352 |   1152 |         1152 |    6 |     51.3% |     3.48
  48 |      2352 |   1152 |         1152 |    8 |     51.3% |     3.09
========================================================================

注意:这个简化模型只算了路由器数量,没有计入 ShuffleBox(被动器件,不算路由器)。AWS 宣称 69% 的削减,说明他们的实际胖树层级更深、k 值更大,或者 ShuffleBox 替代了更多中间层设备。度数越高,平均跳数越低,但线缆复杂度上升——AWS 选择的度数是在跳数和布线成本之间的平衡点。

路由怎么做:随机图上的流量工程

扁平随机图最大的运维挑战是路由。胖树的层级路由是确定性的,随机图上路径多且随机,传统 ECMP 不够用。AWS 没在摘要中披露具体路由协议细节,但基于随机图理论的网络通常采用以下策略:

  • 多路径负载均衡:随机图天然提供大量等价短路径,比胖树的 ECMP 路径数更多,流量分散更均匀。
  • 集中式路径计算:SDN 控制器掌握全局拓扑,按随机图结构计算最优路径分配,避免局部拥塞。
  • 故障快速收敛:随机图的冗余度让单链路故障的影响面很小,控制器重算路径的代价低。

如果你想在自己的环境里实验随机图路由,可以用一个最小化的网络模拟——下面是用 NetworkX 做路径分析的示例:

#!/usr/bin/env python3
"""
random_graph_routing.py
在准随机图上模拟多路径路由和故障收敛。
依赖: pip install networkx
运行: python3 random_graph_routing.py
"""

import networkx as nx
import random

def build_quasi_random_graph(num_tors: int, degree: int) -> nx.Graph:
    """构建准随机图拓扑"""
    G = nx.random_regular_graph(d=degree, n=num_tors, seed=42)
    return G

def analyze_routing(G: nx.Graph, src: int, dst: int):
    """分析两个 ToR 之间的短路径数量和跳数分布"""
    all_paths = list(nx.all_shortest_paths(G, src, dst))
    hop_count = len(all_paths[0]) - 1 if all_paths else float("inf")
    print(f"  {src} -> {dst}: {len(all_paths)} 条最短路径, "
          f"跳数={hop_count}")
    return all_paths

def simulate_link_failure(G: nx.Graph, src: int, dst: int, fail_link: tuple):
    """模拟单链路故障后的路由收敛"""
    G_failed = G.copy()
    u, v = fail_link
    if G_failed.has_edge(u, v):
        G_failed.remove_edge(u, v)
    try:
        new_path = nx.shortest_path(G_failed, src, dst)
        new_hop = len(new_path) - 1
        print(f"  链路 {fail_link} 故障后: 新路径跳数={new_hop}, "
              f"路径={new_path}")
    except nx.NetworkXNoPath:
        print(f"  链路 {fail_link} 故障后: {src} -> {dst} 不可达!")

if __name__ == "__main__":
    NUM_TORS = 64
    DEGREE = 6

    G = build_quasi_random_graph(NUM_TORS, DEGREE)
    print(f"拓扑: {NUM_TORS} 个 ToR, 度数={DEGREE}")
    print(f"总链路数: {G.number_of_edges()}")
    print(f"全网平均最短路径跳数: "
          f"{nx.average_shortest_path_length(G):.2f}")

    # 正常路由分析
    print("\n--- 正常路由 (取样 5 对 ToR) ---")
    pairs = random.sample(range(NUM_TORS), 10)
    for i in range(0, len(pairs), 2):
        analyze_routing(G, pairs[i], pairs[i+1])

    # 故障模拟
    print("\n--- 单链路故障模拟 ---")
    src, dst = 0, 31
    analyze_routing(G, src, dst)
    # 随机断一条链路
    fail_edge = random.choice(list(G.edges()))
    simulate_link_failure(G, src, dst, fail_edge)

运行后你会看到:64 个 ToR、度数 6 的随机图,平均跳数大约 2.5,任意两点之间有多条最短路径,断一条链路后跳数只增加 1 或保持不变。这就是 AWS 说"resilient"的含义——随机图的容错不是靠冗余核心层,而是靠拓扑本身的路径多样性。

落地考量与边界条件

Resilient Network Graphs 不是万能替换,有几个现实约束:

布线复杂度。 ToR 直连意味着光纤要从每个机架拉到 ShuffleBox,再随机分配到远端机架。机房物理布局必须适配这种布线模式——行间距、光纤走线槽、ShuffleBox 安装位置都需要重新规划。对现有机房的改造成本可能比新建更高,这也是 AWS 只在"大多数新建数据中心"采用的原因。

路由协议成熟度。 随机图上的路由不像胖树那样有成熟的 BGP/OSPF 实践,需要 SDN 控制器或定制路由协议。AWS 有自研的网络控制平面,中小规模数据中心如果照搬架构,路由软件是最大的空白地带。

度数选择是权衡。 度数太低(比如 2-3),跳数上升、容错下降;度数太高(比如 12+),每个 ToR 的端口需求暴增,线缆和 ShuffleBox 成本上升。AWS 选的度数没有公开,但从 69% 的路由器削减反推,度数可能在 4-8 之间。

渐进式采用。 如果你想在自己的环境里尝试类似思路,不需要一步到位:

  1. 先在集群内部做小规模 mesh:选一个 20-30 机架的集群,用 ToR 直连 + 准随机图布线,和胖树集群并行跑,对比延迟和吞吐。
  2. ShuffleBox 可以用便宜替代:小规模场景不需要 AWS 级别的定制光学器件,用多路光纤配线盘 + 手工跳线也能实现类似的随机连接,运维成本高但能验证路由逻辑。
  3. SDN 控制器先行:在物理拓扑改造之前,先用虚拟网络模拟随机图路由,验证控制平面逻辑。

AWS 的 Resilient Network Graphs 证明了一件事:数据中心网络不一定要层级堆叠,随机图的数学特性——短路径、高容错、低节点数——在超大规模下比胖树更经济。但这项架构迁移的门槛不在数学,在物理布线和路由软件。有自研网络栈的大型云厂商可以跟进,中小团队更现实的路径是先吃透随机图的路由逻辑,再逐步改造物理层。


相关推荐