add randomSequence support

This commit is contained in:
Looly 2022-04-06 23:50:56 +08:00
parent 552357cc4e
commit d415696e01
3 changed files with 77 additions and 23 deletions

View File

@ -10,6 +10,7 @@
### 🐣新特性 ### 🐣新特性
* 【core 】 CopyOptions支持以Lambda方式设置忽略属性列表pr#590@Gitee * 【core 】 CopyOptions支持以Lambda方式设置忽略属性列表pr#590@Gitee
* 【core 】 增加中文姓名正则及其校验pr#592@Gitee * 【core 】 增加中文姓名正则及其校验pr#592@Gitee
* 【core 】 Snowflake支持sequence使用随机数issue#I51EJY@Gitee
### 🐞Bug修复 ### 🐞Bug修复
* 【core 】 修复UserAgentUtil识别Linux出错issue#I50YGY@Gitee * 【core 】 修复UserAgentUtil识别Linux出错issue#I50YGY@Gitee

View File

@ -2,6 +2,7 @@ package cn.hutool.core.lang;
import cn.hutool.core.date.SystemClock; import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import java.io.Serializable; import java.io.Serializable;
@ -61,16 +62,30 @@ public class Snowflake implements Serializable {
// 时间毫秒数左移22位 // 时间毫秒数左移22位
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS; private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
// 序列掩码用于限定序列最大值不能超过4095 // 序列掩码用于限定序列最大值不能超过4095
@SuppressWarnings("FieldCanBeLocal")
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);// 4095 private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);// 4095
/**
* 初始化时间点
*/
private final long twepoch; private final long twepoch;
private final long workerId; private final long workerId;
private final long dataCenterId; private final long dataCenterId;
private final boolean useSystemClock; private final boolean useSystemClock;
// 允许的时钟回拨数 /**
* 允许的时钟回拨毫秒数
*/
private final long timeOffset; private final long timeOffset;
/**
* 当在低频模式下时序号始终为0导致生成ID始终为偶数<br>
* 此属性用于限定一个随机上限在不同毫秒下生成序号时给定一个随机数避免偶数问题<br>
* 注意次数必须小于{@link #SEQUENCE_MASK}{@code 0}表示不使用随机数<br>
* 这个上限不包括值本身
*/
private final long randomSequenceLimit;
/**
* 自增序号当高频模式下时同一毫秒内生成N个ID则这个序号在同一毫秒下自增以避免ID重复
*/
private long sequence = 0L; private long sequence = 0L;
private long lastTimestamp = -1L; private long lastTimestamp = -1L;
@ -84,7 +99,7 @@ public class Snowflake implements Serializable {
/** /**
* 构造 * 构造
* *
* @param workerId 终端ID * @param workerId 终端ID
*/ */
public Snowflake(long workerId) { public Snowflake(long workerId) {
this(workerId, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID)); this(workerId, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID));
@ -127,26 +142,30 @@ public class Snowflake implements Serializable {
* @param workerId 工作机器节点id * @param workerId 工作机器节点id
* @param dataCenterId 数据中心id * @param dataCenterId 数据中心id
* @param isUseSystemClock 是否使用{@link SystemClock} 获取当前时间戳 * @param isUseSystemClock 是否使用{@link SystemClock} 获取当前时间戳
* @param timeOffset 允许时间回拨的毫秒数 * @param timeOffset 允许时间回拨的毫秒数
* @since 5.7.3 * @since 5.8.0
*/ */
public Snowflake(Date epochDate, long workerId, long dataCenterId, boolean isUseSystemClock, long timeOffset) { public Snowflake(Date epochDate, long workerId, long dataCenterId, boolean isUseSystemClock, long timeOffset) {
if (null != epochDate) { this(epochDate, workerId, dataCenterId, isUseSystemClock, timeOffset, 0);
this.twepoch = epochDate.getTime(); }
} else{
// Thu, 04 Nov 2010 01:42:54 GMT /**
this.twepoch = DEFAULT_TWEPOCH; * @param epochDate 初始化时间起点null表示默认起始日期,后期修改会导致id重复,如果要修改连workerId dataCenterId慎用
} * @param workerId 工作机器节点id
if (workerId > MAX_WORKER_ID || workerId < 0) { * @param dataCenterId 数据中心id
throw new IllegalArgumentException(StrUtil.format("worker Id can't be greater than {} or less than 0", MAX_WORKER_ID)); * @param isUseSystemClock 是否使用{@link SystemClock} 获取当前时间戳
} * @param timeOffset 允许时间回拨的毫秒数
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) { * @param randomSequenceLimit 限定一个随机上限在不同毫秒下生成序号时给定一个随机数避免偶数问题0表示无随机上限不包括值本身
throw new IllegalArgumentException(StrUtil.format("datacenter Id can't be greater than {} or less than 0", MAX_DATA_CENTER_ID)); * @since 5.8.0
} */
this.workerId = workerId; public Snowflake(Date epochDate, long workerId, long dataCenterId,
this.dataCenterId = dataCenterId; boolean isUseSystemClock, long timeOffset, long randomSequenceLimit) {
this.twepoch = (null != epochDate) ? epochDate.getTime() : DEFAULT_TWEPOCH;
this.workerId = Assert.checkBetween(workerId, 0, MAX_WORKER_ID);
this.dataCenterId = Assert.checkBetween(dataCenterId, 0, MAX_DATA_CENTER_ID);
this.useSystemClock = isUseSystemClock; this.useSystemClock = isUseSystemClock;
this.timeOffset = timeOffset; this.timeOffset = timeOffset;
this.randomSequenceLimit = Assert.checkBetween(randomSequenceLimit, 0, SEQUENCE_MASK);
} }
/** /**
@ -187,10 +206,10 @@ public class Snowflake implements Serializable {
public synchronized long nextId() { public synchronized long nextId() {
long timestamp = genTime(); long timestamp = genTime();
if (timestamp < this.lastTimestamp) { if (timestamp < this.lastTimestamp) {
if(this.lastTimestamp - timestamp < timeOffset){ if (this.lastTimestamp - timestamp < timeOffset) {
// 容忍指定的回拨避免NTP校时造成的异常 // 容忍指定的回拨避免NTP校时造成的异常
timestamp = lastTimestamp; timestamp = lastTimestamp;
} else{ } else {
// 如果服务器时间有问题(时钟后退) 报错 // 如果服务器时间有问题(时钟后退) 报错
throw new IllegalStateException(StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp)); throw new IllegalStateException(StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp));
} }
@ -203,7 +222,12 @@ public class Snowflake implements Serializable {
} }
this.sequence = sequence; this.sequence = sequence;
} else { } else {
sequence = 0L; // issue#I51EJY
if (randomSequenceLimit > 1) {
sequence = RandomUtil.randomLong(randomSequenceLimit);
} else {
sequence = 0L;
}
} }
lastTimestamp = timestamp; lastTimestamp = timestamp;

View File

@ -59,7 +59,7 @@ public class SnowflakeTest {
Set<Long> ids = new ConcurrentHashSet<>(); Set<Long> ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> { ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 5000; i++) { for (int i = 0; i < 50000; i++) {
if(false == ids.add(snowflake.nextId())){ if(false == ids.add(snowflake.nextId())){
throw new UtilException("重复ID"); throw new UtilException("重复ID");
} }
@ -74,4 +74,33 @@ public class SnowflakeTest {
Assert.assertEquals(19, StrUtil.toString(l).length()); Assert.assertEquals(19, StrUtil.toString(l).length());
} }
} }
@Test
@Ignore
public void snowflakeRandomSequenceTest(){
final Snowflake snowflake = new Snowflake(null, 0, 0,
false, Snowflake.DEFAULT_TIME_OFFSET, 2);
for (int i = 0; i < 1000; i++) {
final long id = snowflake.nextId();
Console.log(id);
ThreadUtil.sleep(10);
}
}
@Test
@Ignore
public void uniqueOfRandomSequenceTest(){
// 测试并发环境下生成ID是否重复
final Snowflake snowflake = new Snowflake(null, 0, 0,
false, Snowflake.DEFAULT_TIME_OFFSET, 100);
Set<Long> ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 50000; i++) {
if(false == ids.add(snowflake.nextId())){
throw new UtilException("重复ID");
}
}
});
}
} }