From fe37c1aa847f87b3ddb913f3ff8e3e7f48f1d8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=9A=E5=AE=89=2C=E5=9C=B0=E7=90=83?= <350092686@qq.com> Date: Wed, 25 Oct 2023 03:21:40 +0000 Subject: [PATCH] =?UTF-8?q?!1096=20Snowflake=E5=A2=9E=E5=8A=A0=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=9A=E6=A0=B9=E6=8D=AE=E4=BC=A0=E5=85=A5=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=88=B3=EF=BC=8C=E8=AE=A1=E7=AE=97ID=E8=B5=B7?= =?UTF-8?q?=E7=BB=88=E7=82=B9=20(gitee/issues/I60M14)=20*=20Snowflake=20ad?= =?UTF-8?q?d=20method:=E6=A0=B9=E6=8D=AE=E4=BC=A0=E5=85=A5=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=88=B3-=E8=AE=A1=E7=AE=97ID=E8=B5=B7=E7=BB=88?= =?UTF-8?q?=E7=82=B9=20(gitee/issues/I60M14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/lang/Snowflake.java | 34 +++++++++++++ .../cn/hutool/core/lang/SnowflakeTest.java | 49 +++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java b/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java index c9eeac826..d22f5c90f 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java @@ -198,6 +198,40 @@ public class Snowflake implements Serializable { return (id >> TIMESTAMP_LEFT_SHIFT & ~(-1L << 41L)) + twepoch; } + /** + * 根据传入时间戳-计算ID起终点 + * + * @param timestampStart 开始时间戳 + * @param timestampEnd 结束时间戳 + * @return key-ID起点,Value-ID终点 + * @since 5.8.24 + */ + public Pair getIdScopeByTimestamp(long timestampStart, long timestampEnd) { + return getIdScopeByTimestamp(timestampStart, timestampEnd, true); + } + + /** + * 根据传入时间戳-计算ID起终点 Gitee/issues/I60M14 + * + * @param timestampStart 开始时间戳 + * @param timestampEnd 结束时间戳 + * @param ignoreCenterAndWorker 是否忽略数据中心和机器节点的占位,忽略后可获得分布式环境全局可信赖的起终点。 + * @return key-ID起点,Value-ID终点 + * @since 5.8.24 + */ + public Pair getIdScopeByTimestamp(long timestampStart, long timestampEnd, boolean ignoreCenterAndWorker) { + long startTimeMinId = (timestampStart - twepoch) << TIMESTAMP_LEFT_SHIFT; + long endTimeMinId = (timestampEnd - twepoch) << TIMESTAMP_LEFT_SHIFT; + if (ignoreCenterAndWorker) { + long endId = endTimeMinId | ~(-1 << TIMESTAMP_LEFT_SHIFT); + return Pair.of(startTimeMinId, endId); + } else { + long startId = startTimeMinId | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT); + long endId = endTimeMinId | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | SEQUENCE_MASK; + return Pair.of(startId, endId); + } + } + /** * 下一个ID * diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java index ca02e5e67..be7a9a76b 100755 --- a/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.ConcurrentHashSet; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import org.junit.Assert; import org.junit.Ignore; @@ -19,6 +20,54 @@ import java.util.Set; */ public class SnowflakeTest { + /** + * 测试-根据传入时间戳-计算ID起终点 + */ + @Test + public void snowflakeTestGetIdScope() { + long workerId = RandomUtil.randomLong(31); + long dataCenterId = RandomUtil.randomLong(31); + Snowflake idWorker = new Snowflake(workerId, dataCenterId); + long generatedId = idWorker.nextId(); + // 随机忽略数据中心和工作机器的占位 + boolean ignore = RandomUtil.randomBoolean(); + long createTimestamp = idWorker.getGenerateDateTime(generatedId); + Pair idScope = idWorker.getIdScopeByTimestamp(createTimestamp, createTimestamp, ignore); + long startId = idScope.getKey(); + long endId = idScope.getValue(); + + System.out.println(longToBinaryReadable(generatedId) + " = generatedId longToBinaryReadable"); + System.out.println(longToBinaryReadable(startId) + " = startId longToBinaryReadable"); + System.out.println(longToBinaryReadable(endId) + " = endId longToBinaryReadable"); + // 起点终点相差比较 + long trueOffSet = endId - startId; + // 忽略数据中心和工作机器时差值为22个1,否则为12个1 + long expectedOffSet = ignore ? ~(-1 << 22) : ~(-1 << 12); + System.out.println("计算差值 = " + trueOffSet + ", 预期差值 = " + expectedOffSet); + Assert.assertEquals(trueOffSet, expectedOffSet); + } + + /** + * long转雪花格式的2进制字符串 + * + * @param number long值 + * @return 符号位(1bit)- 时间戳相对值(41bit)- 数据中心标志(5bit)- 机器标志(5bit)- 递增序号(12bit) + */ + private String longToBinaryReadable(long number) { + String binaryString = Long.toBinaryString(number); + StringBuilder sb = new StringBuilder(binaryString); + while (sb.length() < 64) { + sb.insert(0, '0'); // 在二进制字符串前面补零 + } + sb + .insert(52, "-") + .insert(47, "-") + .insert(42, "-") + .insert(1, "-") + ; + return sb.toString(); + } + @Test public void snowflakeTest1(){ //构建Snowflake,提供终端ID和数据中心ID