查了一些雪花算法的资料,带日期格式且长度又满足我们现有长度的规范的不多,所以自己研究了下,直接上代码吧~~!~~
- MySnowflake 雪花算法带日期格式实现,支持配置 数据中心数量, 应用数量, 每毫秒产生流水号个数, 注意下配置数值越大, 流水号长度越大, 返回流水号是字符串格式(因带日期就已经占用8位了)
package com.qzframework.common.uid;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Objects;
/**
* 雪花算法 自定义版本 带年月日
*
* @author 庄金明
*/
public class MySnowflake {
/**
* 默认回拨时间,2S
*/
public static long DEFAULT_TIME_OFFSET = 2000L;
/**
* 数据中心占用位数
*/
private final long dataCenterIdBits;
/**
* 工作机器占用位数
*/
private final long workerIdBits;
/**
* 每毫秒能够生产的流水号个数 占用位数
*/
private final long sequenceBits;
/**
* 根据工作机器占用位数计算出能够支持的最大机器数,2 工作机器占用位数 次方
*/
private final long maxWorkerId;
/**
* 根据数据中心占用位数计算出能够支持的最大数据中心数,2 数据中心占用位数 次方
*/
private final long maxDataCenterId;
private final long workerIdShift;
private final long dataCenterIdShift;
private final long timestampLeftShift;
/**
* 根据每毫秒能够生产的流水号个数 占用位数,计算出每毫秒能够生产几个流水号,2 占用位数 次方
*/
private final long sequenceMask;
/**
* 当日当前机器生成的最大流水号的长度,每日循环使用的,所以最大长度是固定的
*/
private final long datetimeMask;
private final String idFormat;
private final long workerId;
private final long dataCenterId;
/**
* 允许的时钟回拨毫秒数
*/
private final long timeOffset;
/**
* 当在低频模式下时,序号始终为0,导致生成ID始终为偶数<br>
* 此属性用于限定一个随机上限,在不同毫秒下生成序号时,给定一个随机数,避免偶数问题。<br>
* 注意次数必须小于{@link #sequenceMask},{@code 0}表示不使用随机数。<br>
* 这个上限不包括值本身。
*/
private final long randomSequenceLimit;
/**
* 自增序号,当高频模式下时,同一毫秒内生成N个ID,则这个序号在同一毫秒下,自增以避免ID重复。
*/
private long sequence = 0L;
private long lastTimestamp = -1L;
public MySnowflake(long workerId, long dataCenterId, long dataCenterIdBits, long workerIdBits, long sequenceBits) {
this(workerId, dataCenterId, DEFAULT_TIME_OFFSET, dataCenterIdBits, workerIdBits, sequenceBits);
}
public MySnowflake(long workerId, long dataCenterId, long timeOffset,
long dataCenterIdBits, long workerIdBits, long sequenceBits) {
this(workerId, dataCenterId, timeOffset, 0, dataCenterIdBits, workerIdBits, sequenceBits);
}
public MySnowflake(long workerId, long dataCenterId, long timeOffset, long randomSequenceLimit,
long dataCenterIdBits, long workerIdBits, long sequenceBits) {
this.dataCenterIdBits = dataCenterIdBits;
this.workerIdBits = workerIdBits;
this.sequenceBits = sequenceBits;
this.maxWorkerId = -1L ^ (-1L << workerIdBits);
this.maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
this.workerIdShift = sequenceBits;
this.dataCenterIdShift = sequenceBits + workerIdBits;
this.timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
this.sequenceMask = ~(-1L << sequenceBits);
this.workerId = Assert.checkBetween(workerId, 0, maxWorkerId);
this.dataCenterId = Assert.checkBetween(dataCenterId, 0, maxDataCenterId);
this.timeOffset = timeOffset;
this.randomSequenceLimit = Assert.checkBetween(randomSequenceLimit, 0, sequenceMask);
this.datetimeMask = String.valueOf(
(86400000L << timestampLeftShift)
| (this.dataCenterId << dataCenterIdShift)
| (this.workerId << workerIdShift)
| this.sequenceMask).length();
this.idFormat = "%d%02d%02d%0" + this.datetimeMask + "d";
}
/**
* 根据Snowflake的ID,获取机器id
*
* @param id snowflake算法生成的id
* @return 所属机器的id
*/
public long getWorkerId(String id) {
long currId = Long.valueOf(id.substring(8));
return currId >> workerIdShift & ~(-1L << workerIdBits);
}
/**
* 根据Snowflake的ID,获取数据中心id
*
* @param id snowflake算法生成的id
* @return 所属数据中心
*/
public long getDataCenterId(String id) {
long currId = Long.valueOf(id.substring(8));
return currId >> dataCenterIdShift & ~(-1L << dataCenterIdBits);
}
/**
* 根据Snowflake的ID,获取生成时间
*
* @param id snowflake算法生成的id
* @return 生成的时间
*/
public long getGenerateDateTime(String id) {
int year = Integer.valueOf(id.substring(0, 4));
int month = Integer.valueOf(id.substring(4, 6));
int day = Integer.valueOf(id.substring(6, 8));
long currId = Long.valueOf(id.substring(8));
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, 0, 0);
return (currId >> timestampLeftShift) + toDayEpochMilli(localDateTime, ZoneOffset.ofHours(8));
}
/**
* 下一个ID
*
* @return ID
*/
public synchronized String nextId() {
Instant instant = Instant.now();
LocalDateTime now = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
long timestamp = instant.toEpochMilli();
if (timestamp < this.lastTimestamp) {
if (this.lastTimestamp - timestamp < timeOffset) {
// 容忍指定的回拨,避免NTP校时造成的异常
timestamp = lastTimestamp;
} else {
// 如果服务器时间有问题(时钟后退) 报错。
throw new IllegalStateException(StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp));
}
}
if (timestamp == this.lastTimestamp) {
final long sequence = (this.sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
this.sequence = sequence;
} else {
// issue#I51EJY
if (randomSequenceLimit > 1) {
sequence = RandomUtil.randomLong(randomSequenceLimit);
} else {
sequence = 0L;
}
}
lastTimestamp = timestamp;
long timeDiff = timestamp - toDayEpochMilli(now, ZoneOffset.ofHours(8));
long nextId = (timeDiff << timestampLeftShift)
| (dataCenterId << dataCenterIdShift)
| (workerId << workerIdShift)
| sequence;
return String.format(this.idFormat, now.getYear(), now.getMonthValue(), now.getDayOfMonth(), nextId);
}
/**
* 获取当前日期(不包含时分秒)到1970.1.1的毫秒数
*
* @param localDateTime
* @param offset
* @return
*/
private long toDayEpochMilli(LocalDateTime localDateTime, ZoneOffset offset) {
Objects.requireNonNull(offset, "offset");
long epochDay = localDateTime.toLocalDate().toEpochDay();
long secs = epochDay * 86400;
secs -= offset.getTotalSeconds();
return secs * 1000;
}
/**
* 循环等待下一个时间
*
* @param lastTimestamp 上次记录的时间
* @return 下一个时间
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = genTime();
// 循环直到操作系统时间戳变化
while (timestamp == lastTimestamp) {
timestamp = genTime();
}
if (timestamp < lastTimestamp) {
// 如果发现新的时间戳比上次记录的时间戳数值小,说明操作系统时间发生了倒退,报错
throw new IllegalStateException(
StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp));
}
return timestamp;
}
/**
* 生成时间戳
*
* @return 时间戳
*/
private long genTime() {
return Instant.now().toEpochMilli();
}
public static void main(String[] args) {
MySnowflake mySnowflake = new MySnowflake(7L, 1L, 1L, 3L, 6L);
String suid = "";
long start = Instant.now().toEpochMilli();
for (int i = 0; i < 100000; i++) {
suid = mySnowflake.nextId();
if (i == 0) {
System.out.println(suid);
}
}
System.out.println("生产 100000 耗时:" + (Instant.now().toEpochMilli() - start));
System.out.println(mySnowflake.nextId());
String uid = mySnowflake.nextId();
System.out.println(mySnowflake.getGenerateDateTime(uid));
System.out.println(mySnowflake.getWorkerId(uid));
System.out.println(mySnowflake.getDataCenterId(uid));
}
}
- UidProperties 配置属性
package com.qzframework.common.uid;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* 配置属性
*
* @author 庄金明
*/
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = "qzframework.uid")
public class UidProperties {
private long dataCenterIdBits = 1L;
private long workerIdBits = 3L;
private long sequenceBits = 6L;
// ex:
// * qzframework:
// * uid:
// * dataCenterIdBits: 1L
// * workerIdBits: 3L
// * sequenceBits: 6L
// * snowflakeIds:
// * - address: 10.10.10.1
// * workerId: 1
// * dataCenterId: 1
private List<SnowflakeId> snowflakeIds;
@Data
@Accessors(chain = true)
public static class SnowflakeId {
/**
* IP地址
*/
private String address;
/**
* 终端ID (0-31) 单机配置0 即可。 集群部署,根据情况每个实例自增即可。
*/
private Long workerId = 0L;
/**
* 数据中心ID (0-31) 单机配置0 即可。 集群部署,根据情况每个实例自增即可。
*/
private Long dataCenterId = 0L;
}
}
- UidConfig 配置类
package com.qzframework.common.uid;
import cn.hutool.core.lang.Assert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
/**
* uid配置
*
* @author 庄金明
**/
@Configuration
public class UidConfig {
@Bean
public UidProperties uidProperties() {
return new UidProperties();
}
@Bean
public MySnowflake mySnowflake(UidProperties uidProperties) throws UnknownHostException {
String hostName = InetAddress.getLocalHost().getHostName();
Assert.notEmpty(hostName, "fail to get host name");
List<UidProperties.SnowflakeId> snowflakeIds = uidProperties.getSnowflakeIds();
InetAddress[] inetAddresses = InetAddress.getAllByName(hostName);
UidProperties.SnowflakeId snowflakeId = snowflakeIds.stream().filter(
item -> Arrays.stream(inetAddresses)
.anyMatch(inetAddress -> item.getAddress().equals(inetAddress.getHostAddress())))
.findFirst().orElse(null);
Assert.notNull(snowflakeId, "Configuration Error:qzbankframework.uid.snowflakeIds");
return new MySnowflake(snowflakeId.getWorkerId(), snowflakeId.getDataCenterId(),
uidProperties.getDataCenterIdBits(), uidProperties.getWorkerIdBits(), uidProperties.getSequenceBits());
}
}