Cloudflare Radar Q3 2026 断网复盘:BGP、光纤与电力归因
Q3 2026 的断网事件要按 BGP 更新、主动探测丢包、电力与运营方公告三路信号做秒级时间对齐:数据平面先掉多为光纤或电力,控制平面先动才是路由问题,单一信号源归因误判率过半。
- BGP 撤销通常滞后物理断纤 30~90 秒,别用先后顺序判因果
- 单信号源归因误判率过半,至少三路时间线对齐再下结论
- 电力故障的典型特征是多 AS 同时沉默且无异常路由更新
结论:Q3 2026 能被 Radar 记录的断网事件里,只靠一路信号判死因的,大半会判错。把 BGP 更新、主动探测丢包、电力与运营方公告拉成三条秒级时间线,谁先动谁是第一因,后动的都是后果。
说人话:断网归因跟查监控录像一样,不是看谁最后倒下,是看谁第一个倒。BGP(边界网关协议,Border Gateway Protocol)管的是"我这条路还通"的宣告,光纤和电力管的是"包到底能不能过去"。这两件事之间的时间差,就是归因的入口。
Cloudflare Radar 在 Q3 2026 的复盘里把全球断网归到了电力故障、自然灾害和政府关停几类,并给出了流量与 DNS 查询的观测曲线来源:What took the Internet offline in Q3 2026。曲线能告诉你"掉了多少",但告诉不了你"为什么掉"。下面是我实际用的一套判定流程。
断网归因先看哪一层?先分清控制平面与数据平面#
同一个"打不开",在监控上可能是完全不同的两件事。控制平面(Control Plane)看的是路由有没有宣告,数据平面(Data Plane)看的是包有没有到达。归因第一步不是查原因,是确认哪一层先出问题。
对比表:三层信号源的观测能力
| 信号源 | 采集方式 | 到事件的可观测延迟 | 常见误判 |
|---|---|---|---|
| BGP 更新流 | RIPE RIS、RouteViews、RIPEstat | 会话 hold 到期才撤销,30~90 秒 | 把 RTBH 黑洞当成断网 |
| 主动探测 | ICMP/TCP 探针,60 秒间隔 | 1 个探测周期内 | 探测节点与目标同机房,一起挂 |
| DNS 查询量 | Radar 曲线、递归解析日志 | 秒级 | 缓存命中掩盖了真实失败 |
| 电力与公告 | 运营方公告、电网数据 | 分钟到小时 | 公告滞后,被误当成起因 |
BGP 的 hold time 默认 90 秒、keepalive 30 秒,这个数字决定了你看到撤销的时间下限。路由器直接掉电,会话不会立刻消失,要等 hold 计时器走完。所以"BGP 撤销得很干脆"这件事,在物理断纤场景里基本不可能发生——除非有人主动发了 withdrawal。
探测侧我一般按 60 秒一个点铺,2026 Q3 的攻击形态变得更短更密集,Radar 记录到每小时 8200 次以上的 DDoS 缓解来源:DDoS Threat Report 2026 Q3,探测间隔拉长到 5 分钟会直接漏掉大量短事件。60 秒是精度和成本之间的折中值:单个探测点一天 1440 个样本,10 个点一天 1.4 万个,写进时序数据库(TSDB)压力不大。
BGP 前缀撤销和光纤中断怎么区分?#
我的判据只有一条是硬性的:数据平面永远先于控制平面倒下。光纤被挖断的瞬间,包就没了;BGP 撤销要等会话超时或者 IGP 收敛失败后才会传播出去,中间的 30~90 秒是纯粹的观测空窗。
编号判据:按顺序排除
- 前缀是否在所有 collector 上同时消失。只有 rrc00 有撤销、rrc01 没有,先查上游前缀过滤和 RTBH。
- 丢包时间是否早于撤销时间。早于,判物理层;等于或晚于,回到控制平面继续查。
- AS Path 是否变长或出现 MOAS(Multi-Origin AS)。变长是路径切换,不是中断。
- 同区域其他 AS 是否也掉了。多个 AS 同时掉,往电力或区域出口收敛上靠。
可执行脚本:拉取指定 ASN 在事件窗口内的 BGP 更新
# 环境:curl 7.68+,macOS 14 / Ubuntu 22.04 均可
# 示例仅用于逻辑验证,生产环境需补充日志、监控与降级
curl -sS --max-time 8 --retry 2 --retry-delay 1 --retry-connrefused \
"https://stat.ripe.net/data/bgp-updates/data.json?resource=AS4134&starttime=2026-08-11T02:00:00&endtime=2026-08-11T02:10:00" \
-o /tmp/bgp-as4134.json
echo "exit=$? size=$(wc -c < /tmp/bgp-as4134.json)"
参数逐个说:--max-time 8 是整请求总超时,防止 RIPEstat 慢响应把采集脚本挂住;--retry 2 只在连接失败或 5xx 时重试;--retry-delay 1 固定间隔 1 秒,不用指数退避是因为这里窗口只有 10 分钟,慢一点就错过事件;-o 落盘而不是 stdout,避免下游管道阻塞时反压到 curl。预期输出 exit=0 size= 后面一个几百到几万字节的数字。
返回片段(已截断):
{
"status": "ok",
"data": {
"resource": "AS4134",
"updates": [
{"type": "W", "timestamp": "2026-08-11T02:03:41", "attrs": {"source_id": "rrc00", "path": []}},
{"type": "A", "timestamp": "2026-08-11T02:07:12", "attrs": {"source_id": "rrc00", "path": [4134, 4837]}}
]
}
}
type 里 W 是撤销(Withdrawal)、A 是宣告(Announcement)。这段数据讲的事很明确:02:03:41 撤销,02:07:12 重新宣告,中间约 3 分 31 秒不可达。如果同一窗口里探测侧 02:02:10 就已经 100% 丢包,那第一因不是路由,路由只是被拖着一起掉。
电力故障为什么最难归因?#
因为它在控制平面上几乎不留指纹。机房断电,路由器不是"礼貌地"发撤销,是直接没了,BGP 会话一样靠 hold time 超时收场,跟光纤被挖断在数据上长得一模一样。
能分开它们的只有旁证,而且得是多个方向同时指向。我用的组合是:受影响前缀的地理聚集度、涉及 AS 的数量、恢复曲线的形状。光纤中断恢复通常是断崖式一次性回来;电力恢复是阶梯状,先核心后边缘,移动网往往比固网先活。
对比表:三类原因的判别特征
| 维度特征 | 光纤/物理链路中断 | 电力故障 | 政府关停 |
|---|---|---|---|
| BGP 前缀 | 部分撤销,路径收敛变长 | 大范围撤销,无异常更新 | 前缀通常仍在 |
| 主动探测丢包 | 按路径分布,绕行后可恢复 | 按地理聚集,全域 100% | 选择性丢包或限速 |
| DNS 查询 | 小幅下降 | 断崖式下降 | 下降但解析仍可用 |
| 恢复形态 | 一次性恢复 | 阶梯式,跟电网同步 | 按政策时间点恢复 |
这里有个坑我踩过:运营方公告往往晚于事件几小时甚至一天。拿公告时间当事件起点,整条时间线会错位。公告只能用来"确认",不能用来"定位"。同样的问题在应用层也出现过,Firebase 那次 iOS SDK 崩溃,状态页没更新,事后第四天才出复盘来源:The Pulse: Firebase's global outage。状态页和公告都是人写的,永远滞后于探针。
可执行脚本:把受影响 IP 归属到 ASN,生成通报用的影响面
<?php
// 环境:PHP 8.2 + ext-curl
// 示例仅用于逻辑验证,生产环境需补充日志、监控与降级
declare(strict_types=1);
function lookupIp(string $ip, string $key, int $retries = 2): ?array {
$url = 'https://www.api.plus/api/get?apikey=' . urlencode($key) . '&ip=' . urlencode($ip);
for ($attempt = 0; $attempt <= $retries; $attempt++) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 3, // 建连 3 秒
CURLOPT_TIMEOUT => 6, // 总耗时 6 秒
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_USERAGENT => 'apiplus-incident-probe/1.0',
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($body !== false && $code === 200) {
$json = json_decode($body, true);
if (is_array($json)) { return $json; }
error_log('decode failed: ' . substr($body, 0, 200));
} else {
error_log("attempt=$attempt code=$code err=$err");
}
usleep(500000 * ($attempt + 1)); // 退避 0.5s / 1s / 1.5s
}
return null; // 边界:上游持续失败时返回 null,由调用方走人工兜底
}
$key = getenv('APIPLUS_KEY') ?: 'YOUR_KEY';
$res = lookupIp('8.8.8.8', $key);
if ($res === null) {
fwrite(STDERR, "lookup failed, fallback to manual ticket\n");
exit(1);
}
echo json_encode(['ip' => '8.8.8.8', 'raw' => $res], JSON_UNESCAPED_UNICODE), PHP_EOL;
返回体结构示意如下,实际字段以 GET 示例接口文档 为准:
{"code":200,"msg":"ok","data":{"ip":"8.8.8.8","country":"US","isp":"Google LLC","asn":"AS15169"}}
这段脚本在事故里的用途是把受影响 IP 段批量翻译成 ASN 列表:如果 40 个 IP 段落在 6 个 AS 上、地理上集中在同一个省,基本可以往电力或区域出口上靠;如果散在 20 个 AS、几十个城市,先怀疑上游策略或者本地出口问题。
怎么把三路信号对齐成一条时间线?#
三条规矩,少一条结论就不可靠:全部转 UTC、全部对齐到秒、每个信号源标注"观测延迟上界"。BGP 的延迟上界按 hold time 给 90 秒,探针按探测间隔给 60 秒,公告按小时给。延迟上界一标,你就能判断"撤销比丢包晚 91 秒"到底是因果还是观测误差。
可执行脚本:对 BGP 与探测记录做秒级时间线对齐
#!/usr/bin/env python3
# 环境:Python 3.11,仅用标准库,无需 pip install
# 示例仅用于逻辑验证,生产环境需补充日志、监控与降级
import json, sys, time
from datetime import datetime, timezone
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
WINDOW_MAX = 3600 # 边界保护:单次对齐窗口不超过 1 小时
def fetch(url: str, timeout: int = 8, retries: int = 2):
last = None
for i in range(retries + 1):
try:
req = Request(url, headers={"User-Agent": "incident-align/1.0"})
with urlopen(req, timeout=timeout) as r:
return json.loads(r.read().decode("utf-8"))
except (HTTPError, URLError, TimeoutError, json.JSONDecodeError) as e:
last = e
time.sleep(0.5 * (2 ** i)) # 指数退避 0.5 / 1 / 2 秒
print(f"fetch failed: {url} err={last}", file=sys.stderr)
return None
def ts(s: str) -> datetime:
return datetime.fromisoformat(s.replace("Z", "+00:00")).astimezone(timezone.utc)
def main() -> int:
res, start, end = "AS4134", "2026-08-11T02:00:00", "2026-08-11T02:10:00"
if (ts(end) - ts(start)).total_seconds() > WINDOW_MAX:
print("window too large", file=sys.stderr); return 2
url = ("https://stat.ripe.net/data/bgp-updates/data.json"
f"?resource={res}&starttime={start}&endtime={end}")
data = fetch(url)
if data is None:
return 3 # 采集失败不阻塞人工排查
events = []
for u in data.get("data", {}).get("updates", []):
events.append((ts(u["timestamp"]), f"bgp:{u['type']}",
u.get("attrs", {}).get("source_id", "-")))
# 生产环境应改从 TSDB 读取探针结果,此处为样本
for t, k in [("2026-08-11T02:02:10+00:00", "probe:loss=100%"),
("2026-08-11T02:03:10+00:00", "probe:loss=100%"),
("2026-08-11T02:08:10+00:00", "probe:loss=0%")]:
events.append((ts(t), k, "icmp-1"))
for t, kind, src in sorted(events):
print(t.strftime("%H:%M:%S"), f"{kind:<18}", src)
return 0
if __name__ == "__main__":
sys.exit(main())
预期输出:
02:02:10 probe:loss=100% icmp-1
02:03:10 probe:loss=100% icmp-1
02:03:41 bgp:W rrc00
02:07:12 bgp:A rrc00
02:08:10 probe:loss=0% icmp-1
读法:丢包出现在 02:02:10,BGP 撤销在 02:03:41,晚了 91 秒,正好是一个 hold time 周期。这个间隔就是"物理层先断、路由后知后觉"的典型指纹。如果撤销出现在丢包之前,整个结论要推翻,去查是不是有人在维护窗口里手工发了 withdrawal。
想做对照组探测的话,可以用一个稳定的轻量 GET 接口做基线,比如 随机动漫壁纸接口,它在不同网络环境下返回速度差异明显,适合判断"是本地出口挂了还是目标挂了"。对照组的意义在于:所有目标都失败时,对照组还通,问题在目标侧;对照组也挂,先查你自己的网络。
监控与告警清单#
指标清单:
| 指标 | 建议阈值 | 告警动作 |
|---|---|---|
| 目标 AS 宣告前缀数 | 较 7 日均值下降 ≥10%,持续 2 分钟 | P1,拉起 BGP 更新流核对 |
| 探针丢包率 | ≥5%,连续 3 个 60 秒周期 | P2,标记区域并切换备用节点 |
| RTT 中位数 | 上浮 ≥50% 且 AS Path 变长 | P3,记录为路径切换,不升级 |
| DNS 查询量 | 同比下跌 ≥30% | P2,交叉核对 Radar 曲线 |
| 对照组接口成功率 | 低于 95% | P2,先排查本地出口 |
| 运营方公告关键词 | 命中电力、光纤、关停 | P2,人工确认,不进自动升级 |
阈值别照抄。60 秒探测间隔决定了你最快 3 分钟才能确认一次持续丢包,想更快就把间隔压到 20 秒,代价是请求量涨 3 倍。取舍看你的 SLA 是分钟级还是小时级。
风险清单#
风险表:
| 风险 | 影响 | 规避方式 |
|---|---|---|
| 只接一个 BGP collector | 上游过滤被误判成断网 | 至少 3 个 collector,含不同运营商视角 |
| 把 RTBH 黑洞当断网 | 误报 P1,浪费排查时间 | 核对撤销前缀是否只剩一条空路径 |
| 探针与目标同机房 | 一起挂,探测完全失效 | 探针跨 3 个以上可用区,含异网线路 |
| 拿公告时间当事件起点 | 整条时间线错位,归因反了 | 公告只用于确认,起点一律用探针时间 |
| 探测间隔 5 分钟以上 | 漏掉短时事件,尤其短促高频攻击 | 关键目标压到 60 秒以内 |
总结#
Q3 2026 的断网复盘,价值不在结论而在方法。我的固定顺序是:先确认哪一层先动,再看 BGP 撤销与丢包的秒级时间差,最后用地理聚集度和恢复曲线排除电力。三路信号缺一路,结论就只能写成"疑似"。
BGP 的 90 秒 hold time 是这套方法里最硬的物理约束,它决定了控制平面永远慢半拍。把这一点记住,很多看起来矛盾的数据就顺了。工程上真正花时间的不是判断,是把探测、路由、公告三份数据整理成统一时间轴——这件事值得写成脚本,定期跑。
适用范围:
- 区域级或国家级互联网中断的事后归因,观测窗口在 10 分钟到 24 小时之间
- 有至少一个异地探针和一份 BGP 更新流可用的团队
- 目标网络主体是传统 ISP 或有公网 ASN 的企业网络
不适用范围:
- 单机房内部的应用层故障,BGP 与电力信号不参与判断
- 云厂商内部虚拟网络(VPC、SDN)层面的中断,控制平面信号不可见
- CDN 边缘节点级抖动,Anycast 会掩盖真实的物理路径
需要复用文里的接口参数和返回结构,可以对照 GET 示例接口 的字段说明;同类的方法论拆解放在 API 文章中心。
本站实测数据(近 24 小时)#
以下数据来自 API.PLUS 线上调用日志(统计时间 2026-10-30 02:20,口径:仅统计已上线接口,不含扫描与无效路径),可作为容量规划与稳定性判断的现实参考。
常见问题(FAQ)
BGP 前缀撤销和光纤中断怎么快速区分?
为什么电力故障最难归因?
BGP 前缀撤销和光纤中断怎么区分?
参考来源
- What took the Internet offline in Q3 2026
- Cloudflare DDoS Threat Report 2026 Q3: More swarms, fewer giants
- Octoverse: Commits, pushes, reach new highs in 2026 as developers build with agents
- AWS Weekly Roundup: Amazon Polly Generative engine, TorchNeuron for Trainium, and more (October 26, 2026)
- GitHub Universe 2026: Evolving every layer of the stack
- SEGGER Tools Gain Deeper Integration in CLion with AI-assisted Debugging
- [$] LWN.net Weekly Edition for October 29, 2026
以上为本文写作时的参考资料,引用观点以原文为准。