ntp时间跳变的影响以及解决思路

2,454 阅读3分钟

背景

时间跳变导致定时任务失效

时间跳变的原因

  • 网络原因:服务器长时间没有和ntp server进行时间同步,导致有偏差
  • ntp服务器配置的ip有问题
  • 服务器的系统时间被人为篡改: 比如date -s

故障检测

  • 时钟跳变后会在/var/log/message有日志, 人工手动执行ntpdate恢复后也会有这条日志
7:27:36 VM-24-3-centos systemd: Time has been changed

故障恢复

  • 查看ntp服务器ip: ntpstat可以查看有上层ntp连通的基本信息
ntpdate 169.254.0.83
 ^@^@ 8 Jan 17:33:17 ntpdate[28930]: adjust time server 169.254.0.83 offset -0.000775 sec`
  • 手动同步一次时间: ntpdate {ntp server ip: 步骤1获取}

ntp时间同步原理

  • ntp只会同步时间到本地的系统时间,硬件时间不会同步,需要用户打开开关
  • ntp服务器同步时间时,只会告诉你utc时间, 然后本地接受到以后,根据自己配置的时区进行转换
  • ntp服务器是层型结构,有顶端的服务器,多层的relay server, 再到客户端
  • 能作为ntp server的机器,是通过原子钟进行测量时间,十分的昂贵。 因此会有很多relay server进行中转,来减少费用

编程时如何规避时间跳变

1: golang的ticket定时任务不受时间跳变影响

  • 调用系统调用 clock_gettime 获取时钟值(这是 POSIX 时钟)。其中 clockid_t 时钟类型是 CLOCK_MONOTONIC,也就是不可设定的恒定态时钟
  • 恒定态时钟的解释:不可设定的恒定态时钟。 Linux上测量时间始于系统启动,系统启动后就不会发生改变,适用于那些无法容忍系统时钟发生跳跃性变化的应用程序
  • golang测试代码:
	starttime := time.Now()
	tolerance := 5
	cnt := 0
	for {
		<-ticker.C
		cnt++
		interval := int(time.Since(starttime).Seconds())
		//[20*cnt-tolerance, 20*cnt+tolerance]
		if !(interval >= 10*cnt - tolerance && interval <= 10*cnt+tolerance) {
			fmt.Printf("accour time changed. interval:%v, cnt:%v\n", interval, cnt)
		}else {
			fmt.Printf("normal. interval:%v, cnt:%v\n", interval, cnt)
		}
		interval2 := (time.Now().UnixNano() - starttime.UnixNano()) / int64(time.Second)
		fmt.Printf("cur:%v, interval2:%v\n", time.Now().String(), interval2)
	}

结论:

  • golang的time.since(starttime): 得到的时间间隔是准确的, 时间跳变不会影响到这个值---原理:底层使用了单调时钟
  • endtime-starttime: 这种写法得到的间隔是会受时间跳变影响, 可以用到检测时间跳变

2: 计算作业运行时间: endtime-starttime可能会变为负数

使用time.Since()方法可以获取到真实的时间间隔,这种方法不受时间跳变影响

3: 雪花算法的规避手段

  • 延迟等待, 知道当前时间 < 之前的时间: 对于延时小的服务无法接受: 可以等待2秒,超过2秒就抛出异常
  • 时间戳+自增序号: 如果当前的时间<之前的时间:那么继续使用之前的时间,只不过要在时间的基础上 改变自增序号来确定唯一值,---有个问题:自增序号用完之后时间跳变还没有恢复

参考资料

cloud.tencent.com/developer/s… juejin.cn/post/712247…