跳转到内容

NTP 1991 — 用四个时间戳和一组滤波器,让全网服务器的钟差几毫秒

是什么

NTP 是一篇 17 页论文,它告诉我们:不靠原子钟、不改硬件,仅用一组在网络上来回走的报文加几条滤波规则,就能把世界上数百万台服务器的时钟拉到毫秒级一致。日常类比:lamport-1978 那篇里大家干脆放弃读”几点”,只看”谁先谁后”;spanner-2012 那篇里 Google 给每个机房塞 GPS 加原子钟,硬把误差压到几毫秒。NTP 是中间道路——给每台机器一个上游(“我的钟跟谁对”),让上游再跟更上游对,最顶端那位看着 GPS 或铯原子钟。每对节点之间互发四个时间戳,本地软件解一道”光速不变”的小学应用题,就能估出双方钟差和这条网络的来回时延。

更具体地说:客户端发包写入 T1,服务端收到记 T2,回包前记 T3,客户端收回时记 T4。Mills 假设上下行时延对称,于是: 往返时延 δ = (T4 − T1) − (T3 − T2),钟偏 θ = ((T2 − T1) + (T3 − T4)) / 2。 这两个公式是 NTP 的几何核心。但单次测量噪声大,论文又叠了三层:filter 留 8 次中最好的样本、select 用拜占庭式投票剔除”假票员”(falseticker)、combine 给幸存者按精度加权平均,最后用一个 PLL/FLL 混合反馈环慢慢调本地振荡器的频率而不是猛拨指针。整套机制让”软件时钟”在 1991 年的 Internet 上拿到大约 10 ms 精度——这个精度天花板至今还在影响所有不带专用硬件的分布式系统。

35 年过去,hlc-2014 的物理部分依赖它、Cloudflare/Google 公网 NTP 服务靠它、Linux adjtimex 系统调用就是它——你打开手机看到的时间,多半就是 Mills 这篇论文的孩子。

为什么重要

不理解这篇,下面这些事都没法解释:

  • 为什么 hlc-2014 论文里那个 epsilon ≈ 100 ms 的”NTP 偏差上界”是从哪来的——就是 NTP 选择/滤波后能给的最坏估计
  • 为什么 spanner-2012 必须自己做 TrueTime,而不能复用 NTP——NTP 只能”估”偏差,估不出”上界”,TrueTime 要的是 [earliest, latest] 闭区间
  • 为什么”NTP 不安全”——协议本身没鉴权,被中间人延迟一边就能注入任意钟偏;NTS(RFC 8915)才补上
  • 为什么虚拟机和移动设备上 ntpd 表现不好,chronyd 后来居上——VM 时钟漂移比网络抖动还大,best-of-8 滤波反而采样偏

核心要点

NTP 把”远程对钟”拆成 四件事,每件解决前一件留下的缺口:

  1. stratum 层级造可信源:stratum 0 是 GPS/铯原子钟(不直接联网);stratum 1 是直连参考钟的服务器;stratum 2..15 是它们的下游;16 = 失同步。类比:宫廷里御医只对皇帝看病,地方医生看本地病人。这一层让”哪个上游可信”变成了树形结构问题。

  2. 四时间戳估钟差:客户端 T1 发、服务端 T2 收 / T3 回、客户端 T4 收。假设来回时延对称,钟偏 θ = ((T2−T1)+(T3−T4))/2,时延 δ = (T4−T1)−(T3−T2)。类比:你和远方朋友通信,写下”我下午 3 点寄、你 3:01 收、3:02 回、我 3:03 拿到”,就能反推朋友手表对你慢多少。

  3. 三层统计抗噪:单次测量噪声很大,Mills 叠了三层。filter 在最近 8 个样本里挑 δ 最小的那个(短时延 = 路径不堵 = 钟差估得最准);select 把多个上游给的钟偏区间求交,没交集的扔掉,叫剔除 falseticker;combine 给幸存者按精度加权平均。三步下来,把”网络抖动 + 假上游”两类噪声分别压住。

  4. PLL/FLL 反馈调振荡器:算出的钟差不是直接拨指针——指针猛跳会让上层应用的”时间单调”假设崩。Mills 借数字电路里的锁相环,慢慢调本地振荡器的频率,让钟”漂”过去(slew)而不是”跳”过去(step)。类比:航海家发现自己慢了 3 秒,不是把表猛拨 3 秒,而是接下来一天每秒走快 0.0035‰,悄悄追上。

四件事加起来,NTP 在公网拿到 ~10 ms、LAN 拿到 sub-ms 精度——这个边界由网络抖动决定,不是软件能再压的。

实践案例

案例 1:四时间戳算钟偏(一道初中应用题)

T1 = 客户端发出请求时记下 (本地钟): 12:00:00.000
T2 = 服务端收到时记下 (服务钟): 12:00:00.150
T3 = 服务端回包时记下 (服务钟): 12:00:00.151
T4 = 客户端收到回包时记下 (本地钟): 12:00:00.020
往返时延 δ = (T4 − T1) − (T3 − T2)
= 0.020 − 0.001
= 0.019 秒
钟偏 θ = ((T2 − T1) + (T3 − T4)) / 2
= (0.150 + 0.131) / 2
= 0.1405 秒

结论:客户端钟比服务端慢约 140 ms,这条网络来回 19 ms。前提是双向时延对称——这正是 NTP 在跨国链路上误差变大的根因。

案例 2:falseticker 是怎么被选掉的

假设客户端连了 4 个上游,每个给的钟偏区间(中心 ± 精度)是:

上游 A: [100, 110] ms
上游 B: [102, 112] ms
上游 C: [98, 108] ms
上游 D: [-200, -180] ms ← 这个明显跑偏

NTP 的 select 算法:先求所有区间的交集,找一个能被”过半”上游覆盖的窗口。A、B、C 三家在 [102, 108] 有交集,D 完全在外——D 被标记为 falseticker 丢弃。这是工程化的拜占庭容错:哪怕一个上游被攻陷或硬件坏了,只要不超过半数,系统仍然给出正确钟偏。这也是为什么 NTP 配置至少要 4 个上游,3 个都不够投票

案例 3:和 hlc-2014 的依赖关系

HLC 论文里反复出现 |hlc.l − pt| ≤ epsilon——这个 epsilon 就是 NTP 给出的偏差上界,典型 100 ms。

NTP 完全失效 → HLC 的 l 不再贴近真实时间 → HLC 退化成 Lamport 时钟

HLC 不会崩溃,但失去”时间戳贴近真实时间”这条性质,“恢复到 10 分钟前”这种语义就不准了。这是 hlc-2014 给 NTP 留的优雅降级口——也证明 NTP 不是 HLC 的”内部细节”,而是一条外部假设。

案例 4:Spanner 为什么不能直接用 NTP

NTP 给你一个点估计 θ 加一个精度方差——但没法给你”真值一定在 [a, b] 内”的硬上界。spanner-2012 的 TrueTime 必须给 TT.now() 返回闭区间 [earliest, latest],commit-wait 协议要等过 latest − earliest。NTP 估不出这个上界(中间人攻击 / 不对称路径都让上界发散),所以 Google 才花钱在每机房装 GPS+原子钟。这是 NTP 这篇论文最深的工程教训:软件方案天花板就在网络抖动那一层

踩过的坑

  1. 不对称路径毁所有:NTP 假设上下行时延对称。NAT、跨国海缆、移动网络的上下行往往走不同路由,对称假设破产,估出的 θ 会偏向时延长的那一边。这是 NTP 在公网上误差从 ms 级劣化到 100 ms 级的主因。

  2. leap second 引发回拨:闰秒插入时,某些老 ntpd 实现把时钟硬回拨 1 秒。上层应用一旦假设”时间单调”——比如数据库的 MVCC、日志时间戳——就会崩。Cloudflare / Google 后来用 leap smearing:把这 1 秒”涂抹”到 24 小时内每秒慢一点点,避免回拨。

  3. 单一上游永远验不出 falseticker:select 算法的拜占庭容错只在 ≥4 个上游时生效。家用路由器只配一个 time.windows.com 这种典型反模式——上游一旦被劫持,整个 LAN 时间全错。

  4. VM / 移动设备上的钟漂超过抖动:ntpd 的 best-of-8 filter 假设网络抖动是主噪声。但虚拟机被宿主调度暂停、移动设备进入低功耗状态时,本地振荡器漂移比网络抖动大得多,filter 反而把好样本筛掉。chronyd 重新设计了反馈环和样本权重,在这种环境下表现才追平。

  5. 没有鉴权 → 中间人能注入任意时间:NTP 协议本身明文,攻击者只要在客户端到上游之间延迟单向包,就能让 select 算出错误 θ。NTS (RFC 8915, 2020) 才给 NTP 加了 TLS 握手 + 报文鉴权——但部署率仍低。

适用 vs 不适用场景

适用

  • 通用服务器、PC、移动设备的”知道现在大概几点几分”
  • hlc-2014 这类要”贴近真实时间但不要硬上界”的混合时钟
  • 日志关联、跨服务追踪(毫秒级时间对齐已够用)
  • 跨广域网的低成本时间同步

不适用

  • 需要 external consistency / linearizability → 上 spanner-2012 TrueTime + commit wait
  • 数据中心内 sub-microsecond 同步 → PTP (IEEE 1588) 走硬件时间戳
  • 高频交易 / 监管报送(必须能证明误差上界) → GPS 接收器 + PTP
  • 完全离线环境 → NTP 没用,只能靠本地高稳定振荡器(OCXO / 原子钟)

历史小故事(可跳过)

  • 1981 年:Mills 在 ARPA 项目里搞 Internet 时间同步,最早借 ICMP timestamp 报文。
  • 1985 年:NTP v0 (RFC 958),第一次给协议起名。
  • 1988 / 1989 年:v1 (RFC 1059) / v2 (RFC 1119),stratum 层级和滤波算法成型。
  • 1991 年:本文 IEEE Trans Comm 论文,把过去 10 年的设计思想系统化——4 时间戳、filter/select/combine、PLL/FLL 一套讲完,是今天所有 NTP 教材的源头。
  • 1992 / 2010 年:v3 (RFC 1305) / v4 (RFC 5905),加 IPv6、加 autokey 鉴权(后被废弃)。
  • 2020 年:NTS (RFC 8915) 给 NTP 加 TLS 鉴权,回应 30 年来”中间人能改时间”的批评。
  • 2024 年:David L. Mills 去世,享年 85 岁,他亲手写的 ntpd 仍在数百万台服务器上跑。

学到什么

  1. 软件方案的上限就在物理层抖动那里——NTP ~10 ms 不是 Mills 算法不够好,是网络本身的对称假设撑不住更高精度。Spanner 花硬件钱才能突破这层。
  2. 统计 + 反馈是工程上压噪声的双保险——filter / select / combine 是统计层抗噪,PLL/FLL 是反馈层抗漂移,两层不可缺一。
  3. 降级要优雅、单调性要保——Mills 选择 slew 而不是 step,是”系统不能让上层假设破产”的工程素养。leap smearing 是同一思想 30 年后的延续。
  4. 理论 → 算法 → 工程:1981 (DARPA 试) → 1985 (RFC 958) → 1991 (本文) → 2010 (v4) → 2020 (NTS) ——一个看似简单的”对钟”问题花了 40 年才铺到生产系统并加上鉴权。

延伸阅读

关联

  • lamport-1978 —— 因果序的奠基;NTP 给的是物理序,两者正交
  • hlc-2014 —— 物理部分直接依赖 NTP;NTP 失效时 HLC 退化成 Lamport
  • spanner-2012 —— TrueTime 是 NTP 的硬件升级版,把”估计”换成”有界区间”
  • chandy-lamport-1985 —— 经典快照算法;和 NTP 是分布式时间观的两条互补支线

反向链接

  • amplification-hell-2014 —— Amplification Hell 2014 — 把家用宽带放大成几百 Gbps 的反射攻击
  • cerf-kahn-1974 —— Cerf-Kahn 1974 — 用网关把异构网络拼成一个互联网
  • chandy-lamport-1985 —— Chandy-Lamport 1985 — 分布式系统不停机也能拍一张全家福
  • hlc-2014 —— HLC 2014 — 把逻辑时钟和物理时钟合一,让普通服务器也能拍一致快照
  • lamport-1978 —— Lamport 1978 — 分布式系统里没有”绝对的同时”
  • spanner-2012 —— Spanner 2012 — 用原子钟和 GPS 给全球数据库发时间戳