NotFound9
NotFound9
> SnowflakeIDGenImpl类与ZK的SnowflakeZookeeperHolder在代码层面耦合较紧,作者可考虑采用接口编程的方式引入workerid. > 可参考:https://github.com/automvc/honey/blob/master/src/main/java/org/teasoft/honey/distribution/PearFlowerId.java 因为目前Leaf的Snowflake模式只有Zookeeper作为注册中心这一种实现方式,所以这么写是没有问题的,如果是有多种注册中心的实现方式,肯定是用接口来实现好一些。 你可以看看我fork的这个项目https://github.com/NotFound9/Leaf ,除了官方的Zookeeper模式,还有zk_recycle模式,MySQL作为注册中心的模式,本地配置作为注册中心的模式,就是定义接口来处理依赖。
> 近期可能没抽出来人力做历史PR的merge,实在抱歉,我们尽快在今年Q3处理一下哈 好的,辛苦了,谢谢了!
> 提个小建议,可以把cache.containsKey(dbTag) == false 改成 !cache.containsKey(dbTag) 好的
虽然这样做达到了博客里面介绍的对Zookeeper的弱依赖,但是非首次启动或者重启时,连接zookeeper失败时,也可以正常启动服务,但是首先缺少对之前上报的时间戳校验,而且后续生成分布式ID时,如果还没有连接上zookeeper,定时上报时间戳也会失败。所以如果实在需要实现对Zookeeper的弱依赖,可以在往Zookeeper上报时间戳时,同时异步写入时间戳到本地缓存,这样既实现了对Zookeeper的弱依赖,也避免了由于启动时时间回拨生成的ID重复的情况发生。 
> 是否可以换个思路, 寻找不依赖于时钟的方案呢? > 以下有一种思路, 希望交流. > 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...
你的代码还是有问题,如果请求批量id的数量size大于当前内存中的步长值,会一直循环从数据库获取id,并且由于获取的数量小于要求数量,没法成功返回数据,一直循环下去。至少需要加一个判断,要求请求的size
zookeeper除了起保存上次时间戳的作用,还起给每个ip:port分配一个固定的workId的作用(通过在zookeeper特定路径下创建永久有序节点,序号作为workId),如果不用zookeeper,那么使用新的机器或者新的端口部署leaf服务会没法获得唯一的workId。所以不知道考虑到一些小公司,他们在使用leaf时,需要单独维护一个zookeeper集群,是否代价太大,能否增加以下两种workId分配方式: 1.使用数据库来作为注册中心, 使用数据库替代替代zookeeper,因为一般的后端项目都会使用到数据库的概率,而不一定会有专门维护的zookeeper集群。 2.在项目中显式分配workID 针对一些小公司,他们可能一般部署leaf服务的ip和port不会变化。在项目中的配置文件显式得配置某某ip:某某port对应哪个workId,每次部署新机器,新port的时候在项目中添加这个配置,然后时间戳也每次同步到机器上。 @Yaccc 你好,这两种workId分配方式我已经开发完成,稍后会push到我fork的项目中https://github.com/NotFound9/Leaf 不知道Leaf项目需不需要这两种模式,如果Leaf项目需要这两种模式,我也希望可以给Leaf项目提PR,谢谢了
@hellolvs 你好,原有的模式在连接Zookeeper失败后会使用本地缓存的workerID.properties文件,但是这个文件只存了workId,没有存时间戳,所以还是存在潜在的时间回拨问题,我对这个问题进行了修复,并且增加了很多新的功能支持,你可以看看我的这个项目 https://github.com/NotFound9/Leaf
> 你好,我这里有另外一种处理时钟回拨的思路,具体参考的是:https://www.jianshu.com/p/b1124283fc43,在他的基础上我进行了修改,我的 fork:https://github.com/nekolr/Leaf/tree/ring_snowflake 简书这篇文章里面写的代码是有问题的,单看这种方案的话, 好处也仅仅是时钟回拨时,可以复用一些之前剩余的ID,维持一小段时间,而且能维持的时间也取决于平常的获取ID的QPS,没有真正解决时钟回拨问题。 坏处是仅仅为了在发生时钟回拨时能多维持一段时间,就每次获取ID都需要去使用CAS操作更新数组,开销比较大。而且时间回拨后使用的这些ID的生成时间与实际获取时间不一致。 这种方案还不如为每一个正在运行的节点分配一个备用的机器ID,发生时间回拨时,如果回拨时间较短就休眠等待,回拨时间较长就使用备用的机器ID。使用备用ID时,再次发送回拨的话就抛出异常。