墙上时钟
根据某个日历返回当前的日期与时间。
- Linux 上的 clock_gettime(CLOCK_REALTIME)
- Java 中的 System.currentTimeMills()
会返回 1970-01-01(UTC)的时间戳(秒和毫秒)。
墙上时钟会和 NTP 服务器同步产生跳跃导致一些奇怪的问题。
单调时钟
更适合测量持续时间段(时间间隔),如超时或服务的响应时间。保证总是向前(不会出现墙上时钟的回拨现象)。
- Linux 上的
clock_gettime(CLOCK_MONOTONIC)
- Java 中的
System.nanoTime()
单调时钟多个节点的对比没有任何意义,多路 CPU 可能有单独的计时器,且不与其他 CPU 进行同步。由操作系统进行补偿它们之间的偏差。
NTP 检测到本地石英比时间服务器更快或者更慢,NTP 会调整本地石英的震动频率(摆动),最大幅度为 0.05%。 NTP 并不会直接调整单调时钟向前或回拨 。
如何解决?
t2 的时间真的会比 t1 小吗?
这里就牵涉出 2 个概念:墙上时钟、单调时钟,它们之间有什么区别呢?
- 墙上时钟:通常就是指前面讲到的世界协调时 UTC,校准时间后,可能发生回拨
- 单调时钟:计算机自启动以后经历的纳秒数,不会回拨
t1 = time.now() // 时间发生校准 t2 = time.now() // t2比t1小怎么办? elapsed = t2 - t1
一般我们写的代码,像上面程序调用的「时间 API」,通常获取的时间是墙上时钟,所以,如果时间发生校准,就可能会发生「时光倒流」的情况。
这必然对程序产生很大的影响,怎么解决这个问题呢?
幸运的是,NTP 在校准时间时,提供了 2 种方式:
- ntpdate:一切以服务端时间为准,「强制修改」本机时间
- ntpd:采用「润物细无声」的方式修改本机时间,把时间差均摊到每次小的调整上
也就是说,ntpd 当接收到需要「回拨」的时间时,会让本机时间走得「慢」一点,小步调整,逐渐与服务端的时钟「对齐」,这样一来,本机时间依旧是递增的,避免发生「倒流」。
当我们在配置 ntp 服务时,需要格外注意这种情况。另外,在编写程序时,也要注意调用的时间 API 获取的是哪个时间,避免业务逻辑发生异常。