Skip to main content

时间服务/UUID服务

在过往的项目中经常会遇到由于服务器之间,或与各类设备之间的时间不同步,而造成的各种通讯和数据问题,而且,服务器之间时间同步也是分布式事务的重要条件。此外,我们常用的Snowflake UUID的最大缺陷也是时钟回拨问题。 所以,本框架在时间上进行了重新包装,希望能解决上述时间不同步引起的问题。

时间服务#

本框架在应用启动时从时间源获取时间,然后自行计算时间,不依赖系统时间或服务器时钟时间。下面是本框架支持的三种时间源:

  1. NTP时间源
    默认本框架通过sntp协议与国家标准时间服务器cn.ntp.org.cn、cn.ntp.org,或其他第三方NTP时间源如npt1.aliyun.com等进行通讯,获得准确时间。NTP时间源可以通过配置文件进行设置,见下面的配置实例:
# configuration for dev environment
---
readyWork:
mainNtpHost: ntp2.aliyun.com # 设置主NTP服务器,这里指定阿里云的一台时间服务器
fallbackNtpHost: # 添加备用NTP服务器
- time2.cloud.tencent.com # 腾讯云的一台时间服务器
- cn.ntp.org.cn # 国家标准时间服务器
server:
# This is the default binding address.
ip: 0.0.0.0
# Http port if enableHttp is true.
httpPort: 8080

由于网络环境各不相同,请自行选择延迟最小的NTP服务器。系统启动时会自行同步一次时间,如果条件许可,最好每隔几个小时调用 Ready.syncTime(); 方法进行一次时间同步。 时间同步完成后,系统自行计算时间。所以请不要使用Java原生提供的系统时间获取方法,例如:System.currentTimeMillis()、new Date()等,需要统一使用框架提供的时间获取方法:

  • Ready.now()
  • Ready.instant()
  • Ready.currentTimeMillis()
  • Ready.localDateTime()
  • Ready.localDateTime(ZoneId zoneId)
  • Ready.zonedDateTime()
  • Ready.zonedDateTime(ZoneId zoneId)

后续会进一步增强时间服务,包括集群节点自动校准对时,设备(目前特指医疗设备)时间统一管理等。

注意

需要注意的是,如果手工修改了系统时间或者系统连接互联网进行了时间同步,那么应用再次启动的时候一定确保框架成功进行时间同步。在应用运行中修改系统时间不影响应用获取的时间。 如果虚拟机或操作系统整体休眠,此时JVM也一起休眠了,这种情况下也会导致框架时间的暂停,系统唤醒后时间还停留在休眠前,所以系统运行过程中不要采用休眠的方式暂停服务,应该直接停止服务。

  1. 卫星时间源
    由于NTP时间源需要连接到外网的时间服务器,很多场合无法提供或不允许有互联网连接。此时可以通过北斗或GPS卫星获取时间,目前框架提供基于串口通讯的卫星时间源模块和支持局域网的NTP卫星时间源模块。 这些时间模块可以应用于服务器给本框架提供时间源,还可以应用于向各类物联网设备及其他需要校准时间的硬件设备。

  2. 铷原子钟、铯原子钟时间源
    上面通过互联网NTP服务器或卫星获取时间其实都是远程获取原子钟时间,国家时间服务器也是由原子钟提供的时间,卫星上也有一个原子钟与国家受时中心的原子钟同步对公众提供时间。 如果对时间要求较高,可以在集群内网部署原子钟并通过卫星驯服,向集群提供高精度时间。

UUID服务#

本框架集成了改良版的Snowflake UUID,此UUID方案具有数值意义的顺序性,且较短,占用空间少,索引效率高。缺点是不能出现时钟回拨,通过上面的时间方案,我们的时间有了保障,所以UUID也有了保障。 本框架的改良版的64bits uuid (long)的bit分配如下:

* +------+----------------------+----------------+-----------+
* | sign | delta seconds | worker node id | sequence |
* +------+----------------------+----------------+-----------+
* 1bit 32bits 18bits 13bits

可以支持使用2^32 - 1秒的时长,约136年,从2020-01-01日开始计时。可以支持2^18 - 1个工作节点,即约26万个节点。每个节点每秒可以支持产生2^13 - 1个ID,即每个节点每秒最多8191个ID。 每个节点需要配置工作ID,如果集群规模不大,可以利用系统自带的工作ID自动分配方案:

  1. 根据节点的IPv4地址计算出一个ID,在一个子网内通常不会重复。(配置uuidWorkerId: IP)
  2. 根据节点本地IP的MAC地址计算出一个ID,在一个子网内通常不会重复。(配置uuidWorkerId: MAC,此为默认项)

如果集群规模较大,或者希望自行决定工作ID的分配,可以通过配置文件进行设定一个具体的数值:

# configuration for dev environment
---
readyWork:
uuidWorkerId: 66 # MAC/IP/0-262143, MAC by default
server:
# This is the default binding address.
ip: 0.0.0.0
# Http port if enableHttp is true.
httpPort: 8080

使用方法比较简单:Ready.getId(); 即可返回一个long型唯一ID。