NTP 与时间同步:互联网的心跳
你手机上的时间和服务器上的时间,差多少算正常?这篇讲 NTP 怎么用四个时间戳算出差值、stratum 层级为什么会退化、闰秒为什么这么难缠,以及时间一旦被污染会出什么事。
两台机器都说「现在 12:00:00」,它们说的其实不是同一件事。分布式系统里,日志要按时间排序、缓存要判过期、TLS 证书要判有效、金融交易要定先后 —— 这些全建立在一个前提上:大家的时间是一致的。而时间恰恰是计算机最不擅长的东西之一。

图片来源:ESO,CC BY 4.0,来自 Wikimedia Commons。
石英钟到底有多不准
主板上那颗晶振靠石英的压电谐振计时,成本几分钱。它的频率随温度漂移,普通晶振在整机温度范围内动辄偏 20 ppm —— 也就是每秒钟差 20 微秒,一天下来就是 1.7 秒。一个月能漂出一分钟。

图片来源:Glosome,CC BY-SA 4.0,来自 Wikimedia Commons。
铯原子钟的精度是另一回事:它是用铯-133 原子在两个能级之间跃迁的频率(9,192,631,770 Hz)来定义「一秒」的,这个频率在定义上不漂。问题在于它塞不进你的笔记本 —— 一台上世纪五十年代的实验室铯钟有整整一柜子。

图片来源:Steve Jurvetson,CC BY 2.0,来自 Wikimedia Commons。
所以现实的做法是:让廉价的晶振走,让昂贵的原子钟校。
四个时间戳算一个差值
NTP(Network Time Protocol)跑在 UDP 123 端口,核心就四个数:
| 记号 | 含义 |
|---|---|
| T1 | 客户端发出请求的本地时间 |
| T2 | 服务器收到请求的服务器时间 |
| T3 | 服务器发出响应的服务器时间 |
| T4 | 客户端收到响应的本地时间 |
假设来回路径对称,那么网络往返是 (T4 − T1) − (T3 − T2),而客户端相对服务器快了多少是:
offset = ((T2 − T1) + (T3 − T4)) / 2
客户端把这个 offset 加到自己的时钟上就同步了。注意它算的是差值,不是绝对值 —— NTP 从不要求你知道「现在几点」,只要求你和别人一致。
一次同步只能给出这个瞬间的差值。所以 NTP 客户端不是「校一次就走」,而是持续采样、用统计方法拟合出晶振的频率偏差和漂移率,把时钟驯服成一条平滑的曲线,而不是一次次生硬地跳。这也是为什么 ntpd 会拒绝明显离谱的样本 —— 它怕把已经收敛的曲线带偏。

图片来源:Kristian Mollenborg,CC BY 2.0,来自 Wikimedia Commons。
stratum 是距离,不是精度
NTP 的层级叫 stratum:
- stratum 0:真正的参考时钟 —— 原子钟、GPS 接收机。它们不直接对外服务;
- stratum 1:直接连着 stratum 0 的服务器;
- stratum 2:从 stratum 1 取时间,以此类推,上限 15,16 表示不可用。
常见的误解是「stratum 数字小 = 精度高」。其实它只表示离参考时钟隔了几跳,跟精度没有直接关系。一台负载过重、网络拥塞的 stratum 1 服务器,给出来的时间可能还不如一台网络状况良好的 stratum 3。
公网 NTP 的典型精度是几毫秒到几十毫秒;同一个机房内能到亚毫秒。要做到亚微秒,就得换 PTP(IEEE 1588)—— 它和 NTP 最大的区别是在网卡硬件上打时间戳,绕开了操作系统里那几十微秒的不确定延迟。
闰秒:一个拖了五十年的补丁
原子钟走得极其稳定,但地球自转不稳定。为了让「世界时」和「太阳在天上的位置」对得上,1972 年起人们用闰秒来补差:UTC 偶尔插入一秒。到 2016 年为止一共插了 27 次。
麻烦在于这一秒没法优雅地插入。软件里一个时间戳回退 1 秒,可能意味着:
- 日志时间倒流,排序错乱;
- 定时任务在同一个「时间点」触发两次;
- 带过期时间的缓存条目永远不过期。
2012 年闰秒时,Reddit、Mozilla、LinkedIn 等一批服务出现过故障。各家后来发明了各种绕法:Google 把这一秒摊到 24 小时里慢慢抹平(leap smear),Amazon 干脆用自己的时间源。
2022 年,国际计量大会决定:最迟 2035 年停止插入闰秒。也就是说,未来某个时刻 UTC 和 UT1 会差到大约一分钟 —— 到那时再想别的办法。
时间被污染会出什么事
时间同步是一条信任链,而它经常被忽视:
- NTP 放大攻击:早期 NTP 的
monlist命令,一个几十字节的请求能换回几百倍的响应。2013 到 2014 年,它被用来打出几百 Gbps 的 DDoS。 - 伪造时间源:如果攻击者能应答你的 NTP 请求,他就能让你的时钟偏掉。证书有效期、令牌过期、双因素验证的 TOTP 全部跟着失效。
- 对时依赖本身就是单点:只配一个上游服务器,等于把时间交给了那一台机器。
所以正经的做法是配 4 个以上互不相关的上游(常见的 pool.ntp.org 就是干这个的),让 NTP 的投票算法自己剔除异常源。
我的判断
时间同步是个典型的「平时完全看不见、坏起来查半天」的东西。我自己的做法很简单:
- 服务器一律配 4 个以上不同机构的上游,不写死单台;
- 监控里加一条「与上游的偏移量」告警,阈值设在 100 毫秒 —— 这个量级对绝大多数业务无害,但一旦超过,通常意味着上游或者网络出了问题;
- 需要跨机严格排序的场景,不去依赖物理时钟,改用逻辑时钟或版本号。
物理时钟永远只是个近似。把它当成精确的东西用,才是真正的问题。

评论
…