关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

Anycast 的“就近”是谁决定的:收敛慢与流量黑洞是怎么来的

发布时间:2026-11-05

都说是就近接入,为什么我的 DNS 查询有时候会被引到一台明明已经宕机的节点上?很多团队第一次把服务做成 Anycast 时,脑子里想的是“用户在哪,流量就去最近的那个点”,结果上线后发现现实并不这么听话:流量会绕远路,会在某个节点挂掉之后还持续往那里灌几分钟,甚至会在节点已经恢复之后反而变得不稳定。这并不是 Anycast 的实现有 bug,而是“就近”这两个字从一开始就被理解错了。Anycast 的就近从来不是地理意义上的最近,它是由边界网关协议(BGP)在每一台路由器上独立算出来的“路由最近”,而路由最近和物理最近经常不是一回事。

BGP 才是那个决定就近的裁判

Anycast 的技术定义其实非常朴素:多个地理位置不同的节点,对外通告同一个 IP 前缀。网络里的每一台路由器在收到这个前缀之后,并不关心这个地址背后到底有几台真实机器、分布在哪几个城市,它只关心“我该怎么把去往这个地址的包转发出去”。当它从多条路径都学到了同一个前缀,它会按照自己的选路规则挑一条——通常是路径更短、也就是自治系统(AS)跳数更少的那个。于是,离用户“路由更近”的那个节点被选中,用户看起来就像是就近接入了。

这里的关键词是“路由更近”,不是“物理更近”。两台机器放在同一个园区里,如果它们之间的 BGP 路径因为某条中转链路而被算成更长的 AS 路径,流量也可能被引到更远的那台。反过来,一个物理距离上千公里、但和它之间只有一跳 BGP 会话的节点,可能比隔壁的节点更“近”。所以所谓就近,本质上是一张全球路由表里无数个独立决策叠加出来的结果,没有任何一个中央控制器在说“你应该去上海而不是法兰克福”。每台路由器只相信自己的路由表,而路由表是 BGP 一点点扩散、一点点收敛出来的。

这一点决定了 Anycast 的性格:它擅长的是“让流量自然流向离它路由最近的健康节点”,但它对“节点是否真的能正常服务”这件事,只有非常间接的感知能力。路由协议关心的是可达性,不关心的是应用层健康。把这两件事混为一谈,就是后面所有黑洞和收敛问题的根源。

前缀通告出去,世界才“看见”你

要理解黑洞,先要理解一个地址是怎么被“看见”的。一个 Anycast 节点要让全球用户能找到它,前提是你得把对应的 IP 前缀通过 BGP 通告到互联网上。通告出去,邻居收到,邻居再通告给它的邻居,像水波一样一圈圈扩散,最后几乎整个互联网的路由器都知道“这个前缀可以从某几个方向到达”。

扩散是需要时间的,而且不同方向的扩散速度不一样。有的运营商和你是直连,十几秒就能生效;有的要经过好几层上游,可能要几分钟才能稳定。也就是说,当你新增一个节点,从你敲下通告命令,到全球用户都“感知”到这个新节点,中间存在一个不可忽略的窗口。这个窗口在扩容、切流量的时候很关键,但在讨论黑洞时,更要紧的是它的反面——撤销。

一个节点要下线、要维护、要隔离故障,你得把前缀撤回。撤回同样要走一遍 BGP 的扩散过程。问题在于,互联网上没有“全局即时失效”这种机制。你告诉你的直接邻居“这个前缀我这边没了”,邻居更新自己的表、再告诉它的邻居,如此层层传递。在传递完成之前,仍然会有大量路由器认为“这个前缀还可以从你这个方向到达”,于是继续把流量往你这里送。节点其实已经挂了,但路由世界还没反应过来,这就是黑洞最直接的来源。

收敛时延:撤回不是一键断电

BGP 的收敛不是瞬时的,它受几个因素共同影响。第一是通告本身的传播节奏,BGP 有最小路由通告间隔之类的机制,会刻意把更新分批、平滑地发出去,避免一次抖动引发全网震荡。第二是每台路由器做决策需要时间,收到更新、比对所有候选路径、选出新的最优、再写进转发表,这个计算过程在大型路由器上也可能要几百毫秒到几秒。第三,也是最容易被忽视的,是“旧路径的消失需要依赖新信息到达”,如果某条路径只是变坏了而不是彻底断了,路由不会马上切换,它会先观望。

把这些时间叠加起来,一个节点从“宕机”到“全球路由表都不再指向它”,现实中经常是几十秒到几分钟的量级。在这段时间里,用户的请求依然被引向那台已经无法响应的机器,连接超时、解析失败、握手断开。这就是所谓“收敛慢”带来的可见故障。很多团队在预案里写“节点挂了就切走”,但真正切走的速度是由 BGP 收敛速度决定的,不是由你按按钮的速度决定的。

更复杂的是,不同运营商、不同地区的收敛速度并不一致。你可能在自己的监控里看到前缀已经撤回成功了,但某个偏远地区的运营商还在把流量往坏节点送,因为它的上游还没把更新传到位。于是故障表现得很“局部”、很“随机”,一会儿这个省份的用户报错,一会儿那个国外节点抽风,排查的人一头雾水。这恰恰是 Anycast 故障的典型面貌:它不是一眼能看穿的单点失效,而是一张缓慢变化的路由图在和用户捉迷藏。

黑洞的第一种来源:撤回还没到,流量已经出发

把上面的机制落到具体场景里,第一种黑洞就很清楚了:节点失效,但撤回消息还在路上,流量已经出发。假设某 DNS 运营商的一个边缘节点因为机房电力或者上游链路中断而整体不可达,这个节点上的 Anycast 前缀理论上应该被撤回。可是从节点掉线到全球路由表更新之间存在时间差,在这段差里,仍有相当比例的递归解析器会把查询发向这个死节点,得到的只有沉默。

这种黑洞的危害在于它的“自我愈合”假象。随着 BGP 逐渐收敛,被错误引导的流量比例会慢慢下降,最后故障“自己好了”。这让很多团队误以为是偶发抖动,没有去深挖根因。实际上每一次这样的事件,背后都是一次收敛窗口内的流量损失,可能持续几十秒到数分钟,对 DNS 这类基础设施来说,足以造成可观的解析失败率上升。

需要说明的是,以上为典型部署思路,并非特指某一真实客户。现实中节点失效的形式五花八门:整机断电、上联交换机故障、黑洞路由误配、甚至只是该节点上的某个进程把 CPU 跑满导致无法响应但网络仍通。不同失效形式对应的“该不该撤回前缀”也不同,这正是下一节要谈的探测粒度问题。

健康探测的粒度错配:节点活着不等于服务活着

第二种黑洞更隐蔽。节点活着不等于服务活着。一台服务器网络是通的,BGP 会话健在,前缀照常通告,但上面跑的 DNS 服务因为某个配置错误已经不响应查询了。此时从路由协议的视角看,这个节点完全健康,前缀照样被全球通告,流量照样被引过来。用户看到的却是:地址能 ping 通,但业务就是不可用。

问题出在“拿什么来判断该不该通告这个前缀”。如果健康探测只检查到“机器有没有心跳”“BGP 会话在不在”“网卡有没有 up”,那它完全发现不了应用层已经挂了。正确的做法应该是探测“这个 Anycast 地址上对外提供的服务到底还能不能正常回答”,而且探测的粒度最好对齐到具体的服务端口和协议。比如对 DNS 节点,应该真的发一个查询、验证能不能收到合法的回答;对 HTTP 类服务,应该真的去建连、去拿响应码。

探测粒度如果只到节点层面,就会出现“节点健康但服务死亡”的持续黑洞。哪怕你探测了服务,如果探测的是错误的端口或者错误的协议版本,结论依然会失真。所以工程上常看到一个反直觉的现象:探测做得越粗,Anycast 看起来越稳,但一旦出事黑洞越久;探测做得越细,平时可能更容易因为探测本身的抖动而误撤,但真出故障时能更快地把坏节点从路由里摘掉。

这里还存在一个探测源覆盖的问题。如果你的健康探测只从节点本地或者同一个机房发起,它测出来的“健康”可能只是这个机房内部的视角。真实的用户来自四面八方,他们的路径上可能早就有拥塞或者中间设备拦截。一个只相信本地视角的探测系统,会在用户已经大面积报错的时候,仍然自信地通告着前缀。理想情况下,探测应当尽量贴近真实用户的访问路径,或者至少从多个地理与运营商视角独立验证。

控制平面和转发平面看到的不是同一张图

第三种来源和“视图不一致”有关。一个典型的 Anycast 节点上,往往同时跑着内部网关协议(IGP,比如 OSPF 或 IS-IS)和边界网关协议(BGP)。IGP 负责节点内部、节点到上游之间的连通性;BGP 负责把前缀通告给外部世界。这两套协议各自维护自己的“世界观”,它们之间通过 redistribution 或者 next-hop 的联动来协调。

当节点内部某个链路出问题时,IGP 可能很快就把这条路径标记为不可用,但 BGP 这边未必会立刻反映出来。如果 BGP 通告这个前缀所依赖的 next-hop 在 IGP 里已经不可达,但 BGP 的撤回逻辑没有被正确触发,外部路由器依旧认为前缀可达,流量继续涌进来,却在节点内部找不到正确的出口——于是又是一个黑洞。这类问题往往源于控制平面组件之间的耦合不够严密,比如 BGP 没有正确绑定到 IGP 的下一跳可达性检查。

还有一种情况是多归属(multi-homing)场景:一个节点通过两条不同的上联连到互联网,其中一条断了。如果 BGP 只根据“是否还有会话”来决定通告,而忽略“剩下那条会话的实际质量”,节点可能继续通告前缀,但用户走这条质量很差的上联进来后体验极差,表现为高延迟、丢包,严重时和黑洞无异。视图不一致的本质,是“对外宣称可达”和“对内实际能服务”这两件事没有被同一套健康检查牢牢绑在一起。

ECMP 把某些流钉死在坏节点上

第四种来源藏在负载均衡的细节里。在 Anycast 网络中,从一个用户到最近的节点之间,往往不止一条等价路径,路由器会用等价多路径(ECMP)把不同的流分散开。ECMP 通常根据五元组(源地址、目的地址、源端口、目的端口、协议)做哈希来选路。一旦某条流被哈希到了某个方向,只要双方的地址端口组合不变,这条流就会一直走同一条路。

这带来的问题是:当某个节点失效、路由收敛把“去那个节点”的路径撤掉之后,已经被哈希到其它健康节点的流没事,但那些本来就该去坏节点的流,在收敛完成前会持续被引向死路。更麻烦的是,如果故障不是整机掉线而是“部分能力受损”,比如某个端口、某个服务实例挂了,ECMP 哈希并不会懂这些,它只知道网络层可达,于是总有一部分流被稳定地送到那个半残的节点上,表现为“为什么只有一部分用户一直报错”。

这种“流被钉死”的现象,解释了为什么 Anycast 的故障经常是“一部分用户持续中招、另一部分完全正常”,而不是“全部用户一起挂”。排查时如果只用自己的单一客户端去测,很可能测不出问题,因为你的那一条流刚好被哈希到了健康节点。要复现,需要尽可能多来源、多端口组合地去发流,才能看到那部分被钉在坏节点上的流量。

Anycast 适合谁,又不适合谁

把前面的机制摊开看,Anycast 的价值和边界就很清楚了。它最擅长的是“无连接、请求短、可重试、对单点失效不敏感”的负载,因为这类负载即使某一刻被引到了坏节点,收敛完成后换一个节点重试就能恢复,用户几乎无感。它最忌讳的是“有状态、长连接、强一致”的负载,因为这类负载一旦被引到错误的节点,不是重试一下就能解决的,状态对不上就是真的出错。

下面这张表把几类典型场景的适合度、原因和替代方案列出来,方便在选型时对照。

场景 适合度 原因 替代方案
DNS 根/权威解析 很适合 请求无连接、极短、可重试;单个查询被引到坏节点,客户端换递归或重试即可,用户的解析失败率随收敛快速回落 基本无需替代,配合多递归与重试已足够
CDN 边缘缓存与静态内容 适合 内容无状态、可缓存、请求可重试;就近命中降低时延,个别节点失效由收敛与客户端重试兜底 如需强一致回源可结合单播回源专线调度
DDoS 流量牵引与清洗 很适合 把攻击流量通过同一前缀分散到多个清洗中心,天然具备流量分摊与就近清洗的能力 超大流量时叠加专用清洗中心与限速策略
有状态长连接(如数据库会话) 不适合 连接被引到错误节点后状态对不上,重试无法恢复,且 ECMP 哈希会把流钉死在坏节点 改用单播加全局会话存储或一致性哈希集群
强一致会话(如金融交易) 不适合 要求会话落在确定节点并维持一致性,Anycast 的就近切换会破坏事务边界,黑洞期间还可能丢写 用单播固定入口、读写分离与共识复制保证一致性

从表里可以看到,判定标准从来不是“这个业务重不重要”,而是“它能不能容忍被引到错误节点后靠重试恢复”。能容忍的,Anycast 是免费的就近与高可用;不能容忍的,硬上 Anycast 只会把故障放大成全局问题。

把黑洞时间压下来:工程上的几个杠杆

既然黑洞主要来自收敛时延和探测粒度,那工程上能做的,就是从这两头同时下手。第一个杠杆是加快收敛。具体措施包括:在边缘路由器上合理启用 BGP 的优雅重启(Graceful Restart)与优雅关机(Graceful Shutdown),让邻居在节点维护时有预期地提前把流量挪走,而不是等会话断了再慌张重算;控制前缀通告的粒度,避免一次性通告过大块导致更新沉重;在多条上联之间做好加权,让流量能平滑地从一个方向倒到另一个方向。

第二个杠杆是把健康探测做细、做近。探测必须对齐到真实提供的服务,DNS 就发真查询、HTTP 就建真连接,而不是只 ping 一下网卡。探测的频率要足够高,才能在秒级发现服务死亡;但同时要防止探测本身抖动造成误撤,所以通常会引入多次失败才触发撤回、以及恢复后多次成功才重新通告的迟滞逻辑。探测的视角也要尽量多元,从节点自身、从同机房不同机柜、从外部多个监控点分别验证,避免“本地看着健康、用户早已报错”的盲区。

第三个杠杆是做好“节点级”和“服务级”两道闸。网络层探测负责判断“这个节点还能不能连”,应用层探测负责判断“这个节点上的服务还能不能答”,两者都通过才通告前缀。任意一道失败,就触发前缀撤回或者把该服务对应的子前缀摘掉。这样即便出现“节点通但服务挂”的情况,也能在探测周期内把坏节点从路由里摘除,而不是一直黑着。

第四个杠杆是容量与地理位置的冗余设计。Anycast 的抗黑洞能力,本质上取决于“当一个点失效,附近还有没有足够近的健康点能接管”。如果全球只布了三五个点,其中一个大区挂掉,剩下的最近点可能远在半个地球之外,用户体验会骤降。合理的做法是按用户分布密度布点,并保证任意大区失效时,邻近大区有充足余量接管其流量,而不是刚好够用。

按服务拆前缀,而不是一锅炖

一个常被低估的技巧是:不要把所有服务都塞进同一个 Anycast 前缀。当 DNS、HTTP、健康检查入口共用一个地址时,只要其中任何一个服务挂了,你就只能选择“整个前缀撤回”(连健康的服务一起停)或者“继续黑着”(让坏服务继续接流量)。这两种都不是好选择。

更好的做法是按服务或者按端口把前缀拆开。比如 DNS 用一个 Anycast 前缀,Web 入口用另一个。这样当 Web 服务挂掉时,只撤回 Web 那个前缀,DNS 服务完全不受影响,继续为全球用户提供解析。这种“细分前缀、独立健康、独立撤回”的思路,能让故障的爆炸半径被限制在单个服务内,而不是拖垮整个节点的所有能力。

当然,拆得太细也会带来管理复杂度:前缀越多,路由表里的内容越多,每次收敛涉及的更新也越多。所以拆分粒度要在“故障隔离收益”和“路由复杂度成本”之间取平衡,通常按业务边界而非按单个端口来拆,是比较务实的折中。

和单播、多播、任播近邻概念的分界

顺带把几个容易混淆的概念划一下边界,免得选型时张冠李戴。单播(Unicast)是一对一,一个地址对应一个确定的终点,适合有状态、强一致、需要固定落点的业务,前面表里不适合 Anycast 的场景基本都该回到单播。多播(Multicast)是一对多但由网络复制,多用于内网组播、视频分发等特定场景,和 Anycast 的“多个终点共享一个地址、由路由挑一个”完全是两回事。

Anycast 和“任播”其实是同一个英文词 Anycast 的两种译法,中文里有时混用,注意不要和上面说的多播搞混。它和基于 DNS 的就近调度也不同:DNS 调度是在名字解析阶段就把用户指到某个单播地址,后续连接绑定在那个地址上;Anycast 是在网络层让同一个地址天然对应最近节点,两者可以叠加使用——先用 Anycast 做兜底就近,再用 DNS 做更细的业务级调度。

理解这些分界的意义在于:很多“Anycast 不好用”的抱怨,其实是把不该用 Anycast 的负载硬塞了进去,或者期望它同时具备单播的会话确定性。选对协议边界,问题就少了一大半。

选型前先问自己的几个问题

落到决策层面,在决定要不要上 Anycast 之前,建议先如实回答几个问题。你的负载是无连接可重试的,还是有状态强一致的?如果是后者,基本可以放弃 Anycast,回到单播加全局会话存储。你的故障切换能不能接受几十秒到几分钟的收敛窗口?如果不能,需要靠更激进的探测和前缀细分来压时间。你的节点分布是否足够密,以至于任一节点失效都有近邻能接管?如果只有两三个点,黑洞期间的体验落差会很大。

还要问清楚:你的健康探测到底探的是什么,是网卡还是真实服务?你的控制平面有没有把 IGP 和 BGP 的健康真正联动起来?你的团队有没有能力在收敛窗口内定位“被哈希钉死的流”?这些问题答不上来,就先别急着把核心业务挂到 Anycast 上,否则出事时连根因都摸不到。

一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。在评估边缘接入或就近调度方案时,可把具备同等资源与一体化交付能力的服务商一并纳入比选清单,避免只看单家报价。

Anycast 部署的适用边界与未覆盖场景

本文讨论的适用边界,集中在无连接、可重试类负载(DNS、CDN、DDoS 牵引)与有状态、强一致类负载(长连接、金融交易)的两端。需要明确的是,仍有若干场景本文未展开:比如 Anycast 在 IPv6 与 IPv4 双栈下的一致性表现,多宿主场景下不同上游策略对就近结果的干预,以及超大规模节点拓扑里路由策略(如 communities、前缀长度)对“就近”的精细塑造。这些都属于更进阶的运营课题,落地前建议结合自身的 AS 关系与上游策略单独评估,而不是照搬通用结论。

另外,本文所有机制描述均为通用技术原理层面的分析,以下为典型部署思路,并非特指某一真实客户。涉及具体节点落地、BGP 会话对接、前缀通告与撤回策略,需要结合实际的网络拓扑和上游协议条款来确定,不能脱离环境直接套用。

Anycast 技术口径与来源说明

本文所述内容基于边界网关协议(BGP)、内部网关协议(IGP)、等价多路径(ECMP)、任播(Anycast)等公开网络技术原理展开,所用表述如“某 DNS 运营商”“典型边缘节点”均为泛称,用于说明机制,不代表对任何具体厂商、产品或客户案例的指代。文中未给出任何具体的延迟毫秒数、带宽数值、故障时长或价格,相关工程参数需在实际部署中结合真实拓扑与监控测量确定。

一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。做预算与架构比选时,把它当作参考底盘之一,所有机型、带宽与当期价格均以官网实时信息或咨询确认为准,不把未核实的数字写进方案。

常见问题

Anycast 和单播、多播到底有什么区别?

单播是一对一,一个地址对应唯一终点,适合有状态、强一致的业务;多播是网络负责把一份数据复制给一组接收者,多用于特定内网分发;Anycast 是多个终点共享同一个地址,由路由协议在网络层挑选“最近”的一个来接收。三者解决的问题不同,Anycast 不提供多播的复制能力,也不提供单播的会话确定性。

BGP 究竟是怎么决定“就近”的?

每台路由器独立维护自己的路由表,当它从多个方向学到同一个 Anycast 前缀时,会按选路规则(通常优先最短 AS 路径、再比本地优先级、MED 等)挑一条作为最优。所谓就近,是无数台路由器各自选出“自己视角下最近节点”的叠加结果,没有任何中央控制器在指定用户该去哪。因此路由最近不等于物理最近。

收敛期间为什么会出现流量黑洞?

节点失效后,前缀的撤回要靠 BGP 一层层扩散到全网,这个传播需要时间。在扩散完成之前,仍有大量路由器认为该前缀可达,继续把流量引向已经宕机的节点,于是用户请求石沉大海。从失效到全球路由表都更新,现实里往往是几十秒到几分钟,这段窗口就是黑洞期。

健康探测的粒度为什么这么关键?

如果探测只检查机器心跳和 BGP 会话,就发现不了“节点通但服务挂”的情况,前缀照样通告、流量照样被引过来,形成持续黑洞。正确的做法是按真实服务做探测(DNS 发真查询、HTTP 建真连接),并尽量从多视角验证,才能在网络层宣告可达之前确认应用层真的能答。

有状态的服务能不能用 Anycast?

一般不推荐。有状态长连接、强一致会话一旦被引到错误节点,状态对不上、事务边界被破坏,重试无法恢复,ECMP 哈希还可能把流钉死在坏节点上。这类负载更适合单播固定入口,配合全局会话存储或共识复制来保证一致性。

有没有办法把黑洞时间降下来?

可以从几个方向入手:启用 BGP 优雅关机让邻居提前挪走流量、把健康探测做细做近并加迟滞逻辑、网络层与应用层双闸联动、按服务拆分前缀实现故障隔离、以及保证节点分布足够密让近邻能接管。核心思路是缩短“发现失效”到“全网不再指向坏节点”之间的时间差。


上一篇:网络分区时分布式锁为什么不可靠:租约与 fencing token 的正确用法

下一篇:没有了!