add methods

This commit is contained in:
Looly
2024-05-13 16:32:02 +08:00
parent 478b523c67
commit 73764db818
5 changed files with 196 additions and 51 deletions

View File

@@ -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。
* <p>此常量通过调用{@link IdUtil#getDataCenterId(long)}方法,传入{@link Snowflake#MAX_DATA_CENTER_ID}作为参数,
* 来获取一个默认的数据中心ID。它在系统中作为一个全局配置使用标识系统默认运行在一个最大数据中心ID限定的环境中。</p>
*
* @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生成。
* <p>这个静态常量是通过调用IdUtil的getWorkerId方法使用默认的数据中心ID和Snowflake算法允许的最大Worker ID来获取的。</p>
*
* @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。<br>
* 传入{@link #DEFAULT_WORKER_ID}和{@link #DEFAULT_DATACENTER_ID}作为参数。<br>
* 此单例对象保证在同一JVM实例中获取ID唯一唯一性使用进程ID和MAC地址保证。
*/
public static final Snowflake DEFAULT_SNOWFLAKE = new Snowflake(DEFAULT_WORKER_ID, DEFAULT_DATACENTER_ID);
/**
* 默认的Seata单例使用默认的节点ID。<br>
* 传入{@link #DEFAULT_SEATA_NODE_ID}作为参数。<br>
* 此单例对象保证在同一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);
}
}
}

View File

@@ -33,7 +33,7 @@ import org.dromara.hutool.core.util.RuntimeUtil;
*/ */
public class IdUtil { public class IdUtil {
// ------------------------------------------------------------------- UUID // region ----- UUID
/** /**
* 获取随机UUID * 获取随机UUID
@@ -73,6 +73,10 @@ public class IdUtil {
return UUID.fastUUID().toString(true); return UUID.fastUUID().toString(true);
} }
// endregion
// region ----- ObjectId
/** /**
* 创建MongoDB ID生成策略实现<br> * 创建MongoDB ID生成策略实现<br>
* ObjectId由以下几部分组成 * ObjectId由以下几部分组成
@@ -92,6 +96,10 @@ public class IdUtil {
return ObjectId.next(); return ObjectId.next();
} }
// endregion
// region ----- Snowflake
/** /**
* 获取单例的Twitter的Snowflake 算法生成器对象<br> * 获取单例的Twitter的Snowflake 算法生成器对象<br>
* 分布式系统中有一些需要使用全局唯一ID的场景有些时候我们希望能使用一种简单一些的ID并且希望ID能够按照时间有序生成。 * 分布式系统中有一些需要使用全局唯一ID的场景有些时候我们希望能使用一种简单一些的ID并且希望ID能够按照时间有序生成。
@@ -164,12 +172,64 @@ public class IdUtil {
* 参考:<a href="http://www.cnblogs.com/relucent/p/4955340.html">http://www.cnblogs.com/relucent/p/4955340.html</a> * 参考:<a href="http://www.cnblogs.com/relucent/p/4955340.html">http://www.cnblogs.com/relucent/p/4955340.html</a>
* *
* @return {@link Snowflake} * @return {@link Snowflake}
* @see IdConstants#DEFAULT_SNOWFLAKE
* @since 5.7.3 * @since 5.7.3
*/ */
public static Snowflake getSnowflake() { public static Snowflake getSnowflake() {
return Singleton.get(Snowflake.class); return IdConstants.DEFAULT_SNOWFLAKE;
} }
/**
* 简单获取Snowflake 的 nextId<br>
* 终端ID 数据中心ID 默认为根据PID和MAC地址生成
*
* @return nextId
* @since 5.7.18
*/
public static long getSnowflakeNextId() {
return getSnowflake().next();
}
/**
* 简单获取Snowflake 的 nextId<br>
* 终端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<br> * 获取数据中心ID<br>
* 数据中心ID依赖于本地网卡MAC地址。 * 数据中心ID依赖于本地网卡MAC地址。
@@ -183,14 +243,14 @@ public class IdUtil {
*/ */
public static long getDataCenterId(long maxDatacenterId) { public static long getDataCenterId(long maxDatacenterId) {
Assert.isTrue(maxDatacenterId > 0, "maxDatacenterId must be > 0"); Assert.isTrue(maxDatacenterId > 0, "maxDatacenterId must be > 0");
if(maxDatacenterId == Long.MAX_VALUE){ if (maxDatacenterId == Long.MAX_VALUE) {
maxDatacenterId -= 1; maxDatacenterId -= 1;
} }
long id = 1L; long id = 1L;
byte[] mac = null; byte[] mac = null;
try{ try {
mac = Ipv4Util.getLocalHardwareAddress(); mac = Ipv4Util.getLocalHardwareAddress();
}catch (final HutoolException ignore){ } catch (final HutoolException ignore) {
// ignore // ignore
} }
if (null != mac) { if (null != mac) {
@@ -229,49 +289,59 @@ public class IdUtil {
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
} }
// ------------------------------------------------------------------- NanoId // endregion
// region ----- SeateSnowflake
/** /**
* 获取随机NanoId * 获取默认的SeataSnowflake单例实例。
* *
* @return 随机NanoId * <p>该方法为静态方法无需实例化对象即可调用。它返回的是一个默认配置的SeataSnowflake实例
* @since 5.7.5 * 可以直接用于生成分布式ID。在应用程序中如果未显式设置自定义的SeataSnowflake实例
* 则会使用此默认实例。
*
* @return SeataSnowflake 返回一个默认配置的SeataSnowflake实例。
*
* @see IdConstants#DEFAULT_SNOWFLAKE
*/ */
public static String nanoId() { public static SeataSnowflake getSeataSnowflake() {
return NanoId.randomNanoId(); return IdConstants.DEFAULT_SEATA_SNOWFLAKE;
} }
/** /**
* 获取随机NanoId * 获取SeataSnowflake单例实例。
* *
* @param size ID中的字符数量 * <p>该方法为静态方法无需实例化对象即可调用。它返回的是一个自定义配置的SeataSnowflake实例
* @return 随机NanoId * 可以用于生成具有特定节点ID的分布式ID。
* @since 5.7.5 *
* @param nodeId 节点ID
* @return SeataSnowflake 返回一个自定义配置的SeataSnowflake实例。
*
* @see IdConstants#DEFAULT_SEATA_SNOWFLAKE
*/ */
public static String nanoId(final int size) { public static SeataSnowflake getSeataSnowflake(final long nodeId) {
return NanoId.randomNanoId(size); return Singleton.get(SeataSnowflake.class, nodeId);
} }
/** /**
* 简单获取Snowflake 的 nextId * 简单获取SeataSnowflake 的 nextId<br>
* 终端ID 数据中心ID 默认为1 * NodeId默认为DataCenterId
* *
* @return nextId * @return nextId
* @since 5.7.18
*/ */
public static long getSnowflakeNextId() { public static long getSeataSnowflakeNextId() {
return getSnowflake().next(); return getSeataSnowflake().next();
} }
/** /**
* 简单获取Snowflake 的 nextId * 简单获取SeataSnowflake 的 nextId<br>
* 终端ID 数据中心ID 默认为1 * NodeId默认为DataCenterId
* *
* @return nextIdStr * @return nextIdStr
* @since 5.7.18
*/ */
public static String getSnowflakeNextIdStr() { public static String getSeataSnowflakeNextIdStr() {
return getSnowflake().nextStr(); return getSeataSnowflake().nextStr();
} }
// endregion
} }

View File

@@ -14,7 +14,6 @@ package org.dromara.hutool.core.data.id;
import org.dromara.hutool.core.lang.generator.Generator; import org.dromara.hutool.core.lang.generator.Generator;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.RandomUtil;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@@ -48,7 +47,7 @@ public class SeataSnowflake implements Generator<Long>, Serializable {
// 节点ID长度 // 节点ID长度
private static final int NODE_ID_BITS = 10; private static final int NODE_ID_BITS = 10;
// 节点ID的最大值1023 // 节点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; private static final int TIMESTAMP_BITS = 41;
// 序列号12位表示只允许序号的范围为0-4095 // 序列号12位表示只允许序号的范围为0-4095
@@ -79,7 +78,7 @@ public class SeataSnowflake implements Generator<Long>, Serializable {
* 构造 * 构造
* *
* @param epochDate 初始化时间起点null表示默认起始日期,后期修改会导致id重复,如果要修改连workerId dataCenterId慎用 * @param epochDate 初始化时间起点null表示默认起始日期,后期修改会导致id重复,如果要修改连workerId dataCenterId慎用
* @param nodeId 节点ID * @param nodeId 节点ID, 默认为DataCenterId或随机生成
*/ */
public SeataSnowflake(final Date epochDate, final Long nodeId) { public SeataSnowflake(final Date epochDate, final Long nodeId) {
final long twepoch = (null == epochDate) ? DEFAULT_TWEPOCH : epochDate.getTime(); final long twepoch = (null == epochDate) ? DEFAULT_TWEPOCH : epochDate.getTime();
@@ -117,7 +116,7 @@ public class SeataSnowflake implements Generator<Long>, Serializable {
*/ */
private void initNodeId(Long nodeId) { private void initNodeId(Long nodeId) {
if (nodeId == null) { if (nodeId == null) {
nodeId = generateNodeId(); nodeId = IdConstants.DEFAULT_SEATA_NODE_ID;
} }
if (nodeId > MAX_NODE_ID || nodeId < 0) { 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); 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<Long>, Serializable {
} }
this.nodeId = nodeId << (TIMESTAMP_BITS + SEQUENCE_BITS); 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);
}
}
} }

View File

@@ -54,10 +54,10 @@ public class Snowflake implements Generator<Long>, Serializable {
public static final long DEFAULT_TWEPOCH = 1288834974657L; public static final long DEFAULT_TWEPOCH = 1288834974657L;
private static final long WORKER_ID_BITS = 5L; private static final long WORKER_ID_BITS = 5L;
// 最大支持机器节点数0~31一共32个 // 最大支持机器节点数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; private static final long DATA_CENTER_ID_BITS = 5L;
// 最大支持数据中心节点数0~31一共32个 // 最大支持数据中心节点数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 // 序列号12位表示只允许序号的范围为0-4095
private static final long SEQUENCE_BITS = 12L; private static final long SEQUENCE_BITS = 12L;
// 机器节点左移12位 // 机器节点左移12位
@@ -94,7 +94,7 @@ public class Snowflake implements Generator<Long>, Serializable {
* 构造使用自动生成的工作节点ID和数据中心ID * 构造使用自动生成的工作节点ID和数据中心ID
*/ */
public Snowflake() { 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<Long>, Serializable {
* @param workerId 终端ID * @param workerId 终端ID
*/ */
public Snowflake(final long workerId) { public Snowflake(final long workerId) {
this(workerId, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID)); this(workerId, IdConstants.DEFAULT_DATACENTER_ID);
} }
/** /**

View File

@@ -13,8 +13,6 @@
package org.dromara.hutool.core.data.id; package org.dromara.hutool.core.data.id;
import org.dromara.hutool.core.collection.ConcurrentHashSet; 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.exception.HutoolException;
import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.lang.tuple.Pair; 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<Long> ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 50000; i++) {
if(!ids.add(snowflake.next())){
throw new HutoolException("重复ID");
}
}
});
}
@Test @Test
public void getSnowflakeLengthTest(){ public void getSnowflakeLengthTest(){
for (int i = 0; i < 1000; i++) { for (int i = 0; i < 1000; i++) {