一、时钟的重要性
在讨论时钟问题之前,先看看:为什么时钟这么重要?在一个应用中
- 需要知道一个请求耗费了多长时间?
- 需要知道在某个时间段内要应用每秒钟能处理的请求数?
- 什么时候需要开始处理任务?
- 错误信息是什么时候出现的?
- LWW问题 。。。。
诸如此类的问题,还能提出很多,因此需要一个靠谱的时钟来保证分布式系统里事件的处理不会出错。
二、问题分析
在上面的问题中,注意到时钟主要分为两类:时刻和时间段。时刻可以是当前某个时间点,例如4:30,也可以是2018年4月30日。时间段则是某个操作持续进行的时间,例如某个请求的相应时间。时间段问题在分布式系统并不依赖于服务器之间的协调,所以真正的问题在于如何保证时刻的可靠性。
那么在分布式系统中,如何解决时刻的问题呢?
首先时钟问题来源于网络的不稳定性,因为服务器之间的交流只能通过网络传输,但是网络传输是需要时间的,并且交网络并不可靠。当一台服务器发送消息给另一台服务器时,并不能保证接收服务器能在确定的时间段内接收到信息,这就导致了两台服务器间的时间不可靠性。因此如何在不可靠网络之上建立一个可靠的时间就成了解决的关键。
三、最常见的解决办法
服务器本身是具有时间处理的装置的,但是只能保证单台服务器的时间可靠性,并不能保证服务器与服务器之间的时间是一致的。所以可以使用一台专门处理时间的服务器,或者利用GPS定位进行协调所有服务器的时间,这个解决办法被称为NTP协议。但是NTP协议并不是完美的解决办法:
- 在同步过程中,如果服务器与NTP服务器相差太多,服务器本身会被强制转成与NTP一致,从而可能会导致应用端出现问题。
- 因为需要NTP服务器与服务器之间进行同步,网络问题依然没有解决。
所以在分布式系统里面使用NTP,需要小心的控制NTP服务器,保证NTP服务器不会出现问题(闰秒等问题),并且还要保证服务器内部的应用不会因为垃圾回收等原因导致的停机使得时间出现偏差。
四、有序的时间戳
再仔细的思考下,分布式的时钟问题的核心是什么?要追求的并不是时间的准确性,而是有序的时间戳。当你保证整个系统都是一个有序并且被打上系统的时间戳时,时刻问题本身并不是很重要了。
对于此,Spanner提出了置信区间的概念,保证全球各地的服务器的时钟都会存在一个置信区间[old,new],在这个置信区间内时间是可靠的,各个服务器只需要比较置信区间就可以保证时间可靠性。
当然一般的公司还没有实现开源的spanner,可以使用的是较为简单的统一单调ID时间戳,通过分布式的序列生成,保证ID在整个系统里面是单调,从而可以保证两个事件是可以区分发生时间的早晚。但是依然会存在问题,在高频率的情况下,对于ID的生成的系统的压力太大。
更进一步,不通过统一的ID生成,让各个机器生成自己的ID标识符,这就是Lamport时钟了,通过计数器和节点ID组成一个标识符。如果你有两个序列,则计数器值大者是时间戳大。如果计数器值相同,则节点ID越大,则时间戳越大。通过服务器的同步,更新计数器,从而保证系统内部时间的一致性。
结论
上面关于时间的讨论还是较为简单的,尽量梳理出了一个逻辑线,去阐述在分布式系统里不能忽略时间的重要性。