diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdConstants.java b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdConstants.java
new file mode 100644
index 000000000..bf47a07d4
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdConstants.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.data.id;
+
+import org.dromara.hutool.core.util.RandomUtil;
+
+/**
+ * ID相关常量
+ *
+ * @author Looly
+ * @since 5.8.28
+ */
+public class IdConstants {
+ /**
+ * 默认的数据中心ID。
+ *
此常量通过调用{@link IdUtil#getDataCenterId(long)}方法,传入{@link Snowflake#MAX_DATA_CENTER_ID}作为参数,
+ * 来获取一个默认的数据中心ID。它在系统中作为一个全局配置使用,标识系统默认运行在一个最大数据中心ID限定的环境中。
+ *
+ * @see IdUtil#getDataCenterId(long)
+ * @see Snowflake#MAX_DATA_CENTER_ID
+ */
+ public static final long DEFAULT_DATACENTER_ID = IdUtil.getDataCenterId(Snowflake.MAX_DATA_CENTER_ID);
+
+ /**
+ * 默认的Worker ID生成。
+ * 这个静态常量是通过调用IdUtil的getWorkerId方法,使用默认的数据中心ID和Snowflake算法允许的最大Worker ID来获取的。
+ *
+ * @see IdUtil#getWorkerId(long, long) 获取Worker ID的具体实现方法
+ * @see Snowflake#MAX_WORKER_ID Snowflake算法中定义的最大Worker ID
+ */
+ public static final long DEFAULT_WORKER_ID = IdUtil.getWorkerId(DEFAULT_DATACENTER_ID, Snowflake.MAX_WORKER_ID);
+
+ /**
+ * 默认的节点ID。
+ * 这个方法是静态的,且最终返回的节点ID是基于数据中心ID生成,生成失败则使用随机数
+ */
+ public static final long DEFAULT_SEATA_NODE_ID = generateNodeId();
+
+ /**
+ * 默认的Snowflake单例,使用默认的Worker ID和数据中心ID。
+ * 传入{@link #DEFAULT_WORKER_ID}和{@link #DEFAULT_DATACENTER_ID}作为参数。
+ * 此单例对象保证在同一JVM实例中获取ID唯一,唯一性使用进程ID和MAC地址保证。
+ */
+ public static final Snowflake DEFAULT_SNOWFLAKE = new Snowflake(DEFAULT_WORKER_ID, DEFAULT_DATACENTER_ID);
+
+ /**
+ * 默认的Seata单例,使用默认的节点ID。
+ * 传入{@link #DEFAULT_SEATA_NODE_ID}作为参数。
+ * 此单例对象保证在同一JVM实例中获取ID唯一,唯一性使用进程ID和MAC地址保证。
+ */
+ public static final SeataSnowflake DEFAULT_SEATA_SNOWFLAKE = new SeataSnowflake(DEFAULT_SEATA_NODE_ID);
+
+ /**
+ * 基于网卡MAC地址生成节点ID,失败则使用随机数
+ *
+ * @return nodeId
+ */
+ private static long generateNodeId() {
+ try {
+ return IdUtil.getDataCenterId(SeataSnowflake.MAX_NODE_ID);
+ } catch (final Exception e) {
+ return RandomUtil.randomLong(SeataSnowflake.MAX_NODE_ID + 1);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdUtil.java
index dd3da0cf0..786747f78 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/IdUtil.java
@@ -33,7 +33,7 @@ import org.dromara.hutool.core.util.RuntimeUtil;
*/
public class IdUtil {
- // ------------------------------------------------------------------- UUID
+ // region ----- UUID
/**
* 获取随机UUID
@@ -73,6 +73,10 @@ public class IdUtil {
return UUID.fastUUID().toString(true);
}
+ // endregion
+
+ // region ----- ObjectId
+
/**
* 创建MongoDB ID生成策略实现
* ObjectId由以下几部分组成:
@@ -92,6 +96,10 @@ public class IdUtil {
return ObjectId.next();
}
+ // endregion
+
+ // region ----- Snowflake
+
/**
* 获取单例的Twitter的Snowflake 算法生成器对象
* 分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。
@@ -164,12 +172,64 @@ public class IdUtil {
* 参考:http://www.cnblogs.com/relucent/p/4955340.html
*
* @return {@link Snowflake}
+ * @see IdConstants#DEFAULT_SNOWFLAKE
* @since 5.7.3
*/
public static Snowflake getSnowflake() {
- return Singleton.get(Snowflake.class);
+ return IdConstants.DEFAULT_SNOWFLAKE;
}
+ /**
+ * 简单获取Snowflake 的 nextId
+ * 终端ID 数据中心ID 默认为根据PID和MAC地址生成
+ *
+ * @return nextId
+ * @since 5.7.18
+ */
+ public static long getSnowflakeNextId() {
+ return getSnowflake().next();
+ }
+
+ /**
+ * 简单获取Snowflake 的 nextId
+ * 终端ID 数据中心ID 默认为根据PID和MAC地址生成
+ *
+ * @return nextIdStr
+ * @since 5.7.18
+ */
+ public static String getSnowflakeNextIdStr() {
+ return getSnowflake().nextStr();
+ }
+
+ // endregion
+
+ // region ----- NanoId
+
+ /**
+ * 获取随机NanoId
+ *
+ * @return 随机NanoId
+ * @since 5.7.5
+ */
+ public static String nanoId() {
+ return NanoId.randomNanoId();
+ }
+
+ /**
+ * 获取随机NanoId
+ *
+ * @param size ID中的字符数量
+ * @return 随机NanoId
+ * @since 5.7.5
+ */
+ public static String nanoId(final int size) {
+ return NanoId.randomNanoId(size);
+ }
+
+ // endregion
+
+ // region ----- DataCenterId and WorkerId
+
/**
* 获取数据中心ID
* 数据中心ID依赖于本地网卡MAC地址。
@@ -183,19 +243,19 @@ public class IdUtil {
*/
public static long getDataCenterId(long maxDatacenterId) {
Assert.isTrue(maxDatacenterId > 0, "maxDatacenterId must be > 0");
- if(maxDatacenterId == Long.MAX_VALUE){
+ if (maxDatacenterId == Long.MAX_VALUE) {
maxDatacenterId -= 1;
}
long id = 1L;
byte[] mac = null;
- try{
+ try {
mac = Ipv4Util.getLocalHardwareAddress();
- }catch (final HutoolException ignore){
+ } catch (final HutoolException ignore) {
// ignore
}
if (null != mac) {
id = ((0x000000FF & (long) mac[mac.length - 2])
- | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
+ | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
@@ -229,49 +289,59 @@ public class IdUtil {
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
- // ------------------------------------------------------------------- NanoId
+ // endregion
+
+ // region ----- SeateSnowflake
/**
- * 获取随机NanoId
+ * 获取默认的SeataSnowflake单例实例。
*
- * @return 随机NanoId
- * @since 5.7.5
+ * 该方法为静态方法,无需实例化对象即可调用。它返回的是一个默认配置的SeataSnowflake实例,
+ * 可以直接用于生成分布式ID。在应用程序中,如果未显式设置自定义的SeataSnowflake实例,
+ * 则会使用此默认实例。
+ *
+ * @return SeataSnowflake 返回一个默认配置的SeataSnowflake实例。
+ *
+ * @see IdConstants#DEFAULT_SNOWFLAKE
*/
- public static String nanoId() {
- return NanoId.randomNanoId();
+ public static SeataSnowflake getSeataSnowflake() {
+ return IdConstants.DEFAULT_SEATA_SNOWFLAKE;
}
/**
- * 获取随机NanoId
+ * 获取SeataSnowflake单例实例。
*
- * @param size ID中的字符数量
- * @return 随机NanoId
- * @since 5.7.5
+ *
该方法为静态方法,无需实例化对象即可调用。它返回的是一个自定义配置的SeataSnowflake实例,
+ * 可以用于生成具有特定节点ID的分布式ID。
+ *
+ * @param nodeId 节点ID
+ * @return SeataSnowflake 返回一个自定义配置的SeataSnowflake实例。
+ *
+ * @see IdConstants#DEFAULT_SEATA_SNOWFLAKE
*/
- public static String nanoId(final int size) {
- return NanoId.randomNanoId(size);
+ public static SeataSnowflake getSeataSnowflake(final long nodeId) {
+ return Singleton.get(SeataSnowflake.class, nodeId);
}
/**
- * 简单获取Snowflake 的 nextId
- * 终端ID 数据中心ID 默认为1
+ * 简单获取SeataSnowflake 的 nextId
+ * NodeId默认为DataCenterId
*
* @return nextId
- * @since 5.7.18
*/
- public static long getSnowflakeNextId() {
- return getSnowflake().next();
+ public static long getSeataSnowflakeNextId() {
+ return getSeataSnowflake().next();
}
/**
- * 简单获取Snowflake 的 nextId
- * 终端ID 数据中心ID 默认为1
+ * 简单获取SeataSnowflake 的 nextId
+ * NodeId默认为DataCenterId
*
* @return nextIdStr
- * @since 5.7.18
*/
- public static String getSnowflakeNextIdStr() {
- return getSnowflake().nextStr();
+ public static String getSeataSnowflakeNextIdStr() {
+ return getSeataSnowflake().nextStr();
}
+ // endregion
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/SeataSnowflake.java b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/SeataSnowflake.java
index afa25c01d..e723442e6 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/SeataSnowflake.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/SeataSnowflake.java
@@ -14,7 +14,6 @@ package org.dromara.hutool.core.data.id;
import org.dromara.hutool.core.lang.generator.Generator;
import org.dromara.hutool.core.text.StrUtil;
-import org.dromara.hutool.core.util.RandomUtil;
import java.io.Serializable;
import java.util.Date;
@@ -48,7 +47,7 @@ public class SeataSnowflake implements Generator, Serializable {
// 节点ID长度
private static final int NODE_ID_BITS = 10;
// 节点ID的最大值,1023
- private final int MAX_NODE_ID = ~(-1 << NODE_ID_BITS);
+ protected static final int MAX_NODE_ID = ~(-1 << NODE_ID_BITS);
// 时间戳长度
private static final int TIMESTAMP_BITS = 41;
// 序列号12位(表示只允许序号的范围为:0-4095)
@@ -79,7 +78,7 @@ public class SeataSnowflake implements Generator, Serializable {
* 构造
*
* @param epochDate 初始化时间起点(null表示默认起始日期),后期修改会导致id重复,如果要修改连workerId dataCenterId,慎用
- * @param nodeId 节点ID
+ * @param nodeId 节点ID, 默认为DataCenterId或随机生成
*/
public SeataSnowflake(final Date epochDate, final Long nodeId) {
final long twepoch = (null == epochDate) ? DEFAULT_TWEPOCH : epochDate.getTime();
@@ -117,7 +116,7 @@ public class SeataSnowflake implements Generator, Serializable {
*/
private void initNodeId(Long nodeId) {
if (nodeId == null) {
- nodeId = generateNodeId();
+ nodeId = IdConstants.DEFAULT_SEATA_NODE_ID;
}
if (nodeId > MAX_NODE_ID || nodeId < 0) {
final String message = StrUtil.format("worker Id can't be greater than {} or less than 0", MAX_NODE_ID);
@@ -125,17 +124,4 @@ public class SeataSnowflake implements Generator, Serializable {
}
this.nodeId = nodeId << (TIMESTAMP_BITS + SEQUENCE_BITS);
}
-
- /**
- * 基于网卡MAC地址生成节点ID,失败则使用随机数
- *
- * @return workerId
- */
- private long generateNodeId() {
- try {
- return IdUtil.getDataCenterId(MAX_NODE_ID);
- } catch (final Exception e) {
- return RandomUtil.randomLong(MAX_NODE_ID + 1);
- }
- }
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/Snowflake.java b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/Snowflake.java
index e3ef942de..3c8e7cb4e 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/Snowflake.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/Snowflake.java
@@ -54,10 +54,10 @@ public class Snowflake implements Generator, Serializable {
public static final long DEFAULT_TWEPOCH = 1288834974657L;
private static final long WORKER_ID_BITS = 5L;
// 最大支持机器节点数0~31,一共32个
- private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
+ protected static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private static final long DATA_CENTER_ID_BITS = 5L;
// 最大支持数据中心节点数0~31,一共32个
- private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
+ protected static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
// 序列号12位(表示只允许序号的范围为:0-4095)
private static final long SEQUENCE_BITS = 12L;
// 机器节点左移12位
@@ -94,7 +94,7 @@ public class Snowflake implements Generator, Serializable {
* 构造,使用自动生成的工作节点ID和数据中心ID
*/
public Snowflake() {
- this(IdUtil.getWorkerId(IdUtil.getDataCenterId(MAX_DATA_CENTER_ID), MAX_WORKER_ID));
+ this(IdConstants.DEFAULT_WORKER_ID);
}
/**
@@ -103,7 +103,7 @@ public class Snowflake implements Generator, Serializable {
* @param workerId 终端ID
*/
public Snowflake(final long workerId) {
- this(workerId, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID));
+ this(workerId, IdConstants.DEFAULT_DATACENTER_ID);
}
/**
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/data/id/SnowflakeTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/data/id/SnowflakeTest.java
index 80e2da7fd..d146a2fe3 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/data/id/SnowflakeTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/data/id/SnowflakeTest.java
@@ -13,8 +13,6 @@
package org.dromara.hutool.core.data.id;
import org.dromara.hutool.core.collection.ConcurrentHashSet;
-import org.dromara.hutool.core.data.id.IdUtil;
-import org.dromara.hutool.core.data.id.Snowflake;
import org.dromara.hutool.core.exception.HutoolException;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.lang.tuple.Pair;
@@ -83,6 +81,22 @@ public class SnowflakeTest {
});
}
+ @Test
+ @Disabled
+ public void uniqueTest2(){
+ // 测试并发环境下生成ID是否重复
+ final Snowflake snowflake = IdUtil.getSnowflake();
+
+ final Set ids = new ConcurrentHashSet<>();
+ ThreadUtil.concurrencyTest(100, () -> {
+ for (int i = 0; i < 50000; i++) {
+ if(!ids.add(snowflake.next())){
+ throw new HutoolException("重复ID!");
+ }
+ }
+ });
+ }
+
@Test
public void getSnowflakeLengthTest(){
for (int i = 0; i < 1000; i++) {