分布式ID生成器 全局唯一、语义定制、极速、趋势递增、易于使用 / English
稳定版 | 主要变动 | 发布日期 |
---|---|---|
2.0.1.1 | 引入漏洞提示、依赖升级 | 2023/01/22 |
2.0.0 | 支持语义化定制、支持多例 | 2022/08/21 |
1.2.2 | 引入量身定制的spring-boot-starter | 2022/04/16 |
1.1.6 | 引入另一种开箱即用的GUID获取策略,可根据IP地址末段自动获取GUID | 2022/03/21 |
- 生成全局唯一的ID,适用于分布式环境(以进程为最小工作单元,在单机/伪集群中照常工作)
- ID趋势递增,基于twitter-snowflake算法,针对时钟回退能够有限自愈
- ID支持语义化定制,可在ID长度、节点数量、TPS、使用年限等各方面进行自由定制
- ID同时支持单例和多例,不再局限于单一的ID策略
- 生成速度极快,轻松达到 单机百万级的QPS
- 云原生、虚拟环境友好,对IP、端口的漂移无感
- 制品为袖珍型jar包,依赖极少,易于使用和集成
在你工程的pom.xml中加入如下片段,即可从maven中央仓库获取:
<dependency>
<groupId>com.stun4j</groupId>
<artifactId>stun4j-guid-core</artifactId>
<version>2.0.1.1</version>
</dependency>
<dependency>
<groupId>com.stun4j.boot</groupId>
<artifactId>stun4j-guid-spring-boot-starter</artifactId>
<version>2.0.1.1</version>
</dependency>
切到项目根目录,在控制台执行如下maven命令:
$ mvn clean package
构建完成后,会在各自的target目录中生成stun4j-guid-core-<version>.jar
和stun4j-guid-spring-boot-starter-<version>.jar
,放入你工程的classpath即可。spring-boot工程仅需要boot-starter
这个jar,如果你希望通过low-level api的方式来使用Guid,那么你可以了解并使用core
这个jar。
- 本ID生成算法是时间敏感的,所以集群环境务必开启NTP服务(尽可能做到时钟前向同步),保障总体正确性和可用性
- 采用Zookeeper作为分布式协调者时,客户端采用Curator和ZK进行通信,需要注意Curator和Zookeeper的兼容性问题
- 目前测试下来,Curator 2.13.0 这个版本的兼容性比较好,可兼容Zookeeper 3.4.10+(server版本)
- 如使用Zookeeper 3.5+(server版本),那么至少应搭配Curator 3.3.0+ 版本
- 推荐使用Curator 5.4+ 版本,搭配使用Zookeeper 3.7+(server版本),更少的安全漏洞、更好的组件质量(另外,Zookeeper 3.7- 官方已停止维护)
- 一个集群支持的进程/节点数量的上限是1024,这是经典snowflake算法非常核心的一点,也就是说datacenterId和workerId的取值范围都是 [0,31],所以有1024种组合,在本框架的实现中也充分映射了这个概念,比如对分布式协调者一个namespace下参与者的数量也做了相同的限制
- 直接或间接通过核心库-方式3使用的需额外注意:
- 虽然本框架提供了灵活的pick ip的方式,但严格来说只有类似如下方式才能确保全局唯一性:
LocalGuid.initWithLocalIp("192.168", 1);//表示从本机挑选出 匹配'192.168.1'这个网段的IP LocalGuid.initWithLocalIp("192.168.1");//等价如上
- 本方式的算法其实是取 'IP末段' 来标识节点的,所以只适用于 相同网段 并且 节点数<=256 的场景(原因是 a.受限于节点数<=1024, b.单个ip段的范围是[0,255]),换言之,不同网段下,IP末段可能重复,导致GUID的全局唯一性被破坏,所以现在,我们再来阐明其它几种API用法的问题,如下:
/* * 以下这些使用方式 * 在不确定的网络环境中(如多网卡、多网段),都可能破坏GUID的全局唯一性 */ LocalGuid.initWithLocalIp();//自动挑选本机IP,过于随意(适用于开发、测试) LocalGuid.initWithLocalIp("192.168");//范围过大 LocalGuid.initWithLocalIp("192.168", 1, 2);//范围过大
- 应注意避免一种情况,比如在某个节点上起一个(伪)集群(比如3个进程),因为IP相同,该集群的每个进程又具有独立的本地时钟(仅确保单进程唯一),所以该集群中如果简单使用本框架来提供逻辑上的全局GUID,是无法严格避免重复的
- 虽然本框架提供了灵活的pick ip的方式,但严格来说只有类似如下方式才能确保全局唯一性:
- 承上,作一个延伸讨论,本ID算法保证全局唯一的另一个重要(且隐晦)前提是预期在同1个JVM中维护1个单例,但我们知道,不同的classloader是可以打破这个限制的(即使在同1个JVM中),目前本框架并没有刻意去处理该问题
- 通常来说,不同的classloader往往会被用来进行业务隔离,但当你将不同的classloader联合起来,以集群视角(或者说一个逻辑的业务单元视角)来直接使用本GUID算法,那么就和上述问题4类似了,而且即使是进程粒度的唯一性也会面对此问题(因为classloader的粒度更细)
- 当然,也不必过于担心,当今主流的微服务架构都是1个进程1个业务单元的,规避复杂的classloader带来的潜在影响,规避问题4产生的伪集群或这些问题的变种 都是好的实践
- 再次重申:datacenterId和workerId结合起来被用来唯一标识一个进程or节点,这两者的组合必须是'唯一'的
- 支持更多分布式协调者 如etcd等
- 尽可能克服时间敏感问题
- 极速UUID算法使用了fast-uuid这个项目
本项目采用 Apache Software License, Version 2.0