启动时时钟回拨的问题咨询
你好,就是每次机器启动后会去zookeeper中获取workId列表,判断当前IP+端口是否存在workId,如果有就使用这个workId,并且对该workId上次上报的最大时间戳与当前时间戳比较,如果是当前时间戳较小,那么抛出异常,以此来应对启动时时间回拨的问题,如果启动时连接zookeeper失败,会去本机缓存中读取workerID.properties文件,读取workId进行使用,但是由于workerID.properties中只存了workId信息,没有存储上次上报的最大时间戳,所以没有进行时间戳判断,所以如果机器的当前时间被修改到之前,就可能会导致生成的ID重复,这个问题是否需要进行修复,可以给你们提PR吗?每次在上报时间戳时将时间戳同时写入本机缓存,或者是在启动时连接zookeeper失败时不使用本机缓存,直接抛出异常。
虽然这样做达到了博客里面介绍的对Zookeeper的弱依赖,但是非首次启动或者重启时,连接zookeeper失败时,也可以正常启动服务,但是首先缺少对之前上报的时间戳校验,而且后续生成分布式ID时,如果还没有连接上zookeeper,定时上报时间戳也会失败。所以如果实在需要实现对Zookeeper的弱依赖,可以在往Zookeeper上报时间戳时,同时异步写入时间戳到本地缓存,这样既实现了对Zookeeper的弱依赖,也避免了由于启动时时间回拨生成的ID重复的情况发生。

是否可以换个思路, 寻找不依赖于时钟的方案呢? 以下有一种思路, 希望交流. https://github.com/automvc/honey/blob/master/src/main/java/org/teasoft/honey/distribution/OneTimeSnowflakeId.java
是否可以换个思路, 寻找不依赖于时钟的方案呢? 以下有一种思路, 希望交流. https://github.com/automvc/honey/blob/master/src/main/java/org/teasoft/honey/distribution/OneTimeSnowflakeId.java
你这种方式就是时间那一部分不随系统时间来变化,而是等序列号那一部分用完之后,再让time+1,然后继续让序列号那一部分从0开始。跟百度的uid-generator里面的CachedUidGenerator模式实现原理一样,不过CachedUidGenerator模式做得更加完善一些,定时将id填充到一个环形数组,后面调用时直接从环形数组中去,你这样实时生成,生成效率会低一些,发挥不了这种模式的优势。
这种模式优点是可以预先获取进行缓存,生成效率高,且不依赖系统时钟,不用担心时钟回拨。
这种模式的缺点是id是连续的,如果不增加id抛弃的逻辑的话,会有信息安全的问题,id连续爬虫爬取也会比较容易。信息安全的问题就是竞争对手某一天12点生成几个订单,获得一些订单号,第二天12点再生成一些订单,就大概知道每一个id生成节点这一天发放的id总量是多少,从而推测出一天的订单总量是多少。所以需要额外开发id随机抛弃的逻辑,让id不连续。
我的博客地址:https://juejin.im/user/5b370a42e51d4558ce5eb969 可以关注一下我的博客,最近这几天在写一篇文章《对分布式ID生成框架Leaf和uid-generator的分析与改进》,里面会分析Leaf和uid-generator的优点和缺点,以及改进方法。
秒时间自动加1,确实是雪花类算法改成不依赖时间的关键。(但话从你那说出来,就把人带偏了) uid-generator没有解决时钟回拨问题呀,请看以下源码: protected synchronized long nextId() { long currentSecond = getCurrentSecond();
// Clock moved backwards, refuse to generate uid
if (currentSecond < lastSecond) {
long refusedSeconds = lastSecond - currentSecond;
throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
}
CachedUidGenerator 缓存的数据,从是依赖时钟的算法程序取的,它缓存起来就不依赖时钟啦??这是什么逻辑????
uid-generator与其它类似工具不同之处,主要是它的workerid用了22位,可以多次重启。
id在内存直接生成,相比其它的是耗时很低的。 返回结构Result{id=123456, status=SUCCESS}比直接返回原生long型ID增加的时间,你都不觉得多,怎么会觉得id在内存生成的时间多呢? 缓存ID能省的时间,还不能抵消别的时间开销。建议你实测下各部分的时间消耗。 建议写代码测好,写了文章再贴文章的url出来一起讨论.
OneTimeSnowflakeId不是连续单调递增的,不知你说的是哪? SerialUniqueId才是连续单调递增的(某个workerid内),全局唯一的。
总结: 1.时间自动加1,不再需要从时钟处拿时间值,从而不依赖时钟. 2.OneTimeSnowflakeId 可以批量获取ID值。如果某些业务需要,批量获取效率更高。 3.SerialUniqueId是连续单调递增的(某个workerid内),全局唯一的。可以用在DB表主键自增等场景。 OneTimeSnowflakeId 与leaf的snowflake的主键区别是时间只精确到少,且自增长。每秒还会分成很多段,每段开始的序号,是可以随机的,足以应用在订单号等场景。
秒时间自动加1,确实是雪花类算法改成不依赖时间的关键。(但话从你那说出来,就把人带偏了) uid-generator没有解决时钟回拨问题呀,请看以下源码: protected synchronized long nextId() { long currentSecond = getCurrentSecond();
// Clock moved backwards, refuse to generate uid if (currentSecond < lastSecond) { long refusedSeconds = lastSecond - currentSecond; throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds); }CachedUidGenerator 缓存的数据,从是依赖时钟的算法程序取的,它缓存起来就不依赖时钟啦??这是什么逻辑????
uid-generator与其它类似工具不同之处,主要是它的workerid用了22位,可以多次重启。
id在内存直接生成,相比其它的是耗时很低的。 返回结构Result{id=123456, status=SUCCESS}比直接返回原生long型ID增加的时间,你都不觉得多,怎么会觉得id在内存生成的时间多呢? 缓存ID能省的时间,还不能抵消别的时间开销。建议你实测下各部分的时间消耗。 建议写代码测好,写了文章再贴文章的url出来一起讨论.
OneTimeSnowflakeId不是连续单调递增的,不知你说的是哪? SerialUniqueId才是连续单调递增的(某个workerid内),全局唯一的。
总结: 1.时间自动加1,不再需要从时钟处拿时间值,从而不依赖时钟. 2.OneTimeSnowflakeId 可以批量获取ID值。如果某些业务需要,批量获取效率更高。 3.SerialUniqueId是连续单调递增的(某个workerid内),全局唯一的。可以用在DB表主键自增等场景。 OneTimeSnowflakeId 与leaf的snowflake的主键区别是时间只精确到少,且自增长。每秒还会分成很多段,每段开始的序号,是可以随机的,足以应用在订单号等场景。
我觉得你可以去看看CachedUidGenerator的源码,应该只是启动的时候需要获取系统时间,后续生成id都是根据序列号+1得到的,序列号满了的时候就对时间戳部分+1.
相信大多数人,能找到这个项目都是出于技术的爱好;而技术应该是没有偏见的!