![JAR search and dependency download from the Maven repository](/logo.png)
com.github.kaizen4j.id.UidGenerator Maven / Gradle / Ivy
package com.github.kaizen4j.id;
import com.github.kaizen4j.util.NetworkUtils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Twitter SnowFlake for Java implements
*
* 推荐以单例模式持有实例
*
* @author liuguowen
*/
public class UidGenerator {
private static final Logger logger = LoggerFactory.getLogger(UidGenerator.class);
/** 机器节点 Id */
private long workerId;
/** 数据中心节点 Id */
private long dataCenterId;
/** 序列号 */
private long sequence = 0L;
/** 上一次时间戳 */
private long lastTimestamp = -1L;
/** 起始的时间戳:2021-07-01 12:21:32 */
private static long twepoch = 1625113292000L;
/** 机器节点占用的位数 */
private static long workerIdBits = 5L;
/** 数据中心占用的位数 */
private static long dataCenterIdBits = 5L;
/** 最大支持机器节点数 0~31,一共 32 个 */
private static long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 最大支持数据中心节点数 0~31,一共 32 个 */
private static long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
/** 序列号占用的位数 */
private static long sequenceBits = 12L;
/** 机器节点较序列号的偏移量:12位 */
private static long workerIdShift = sequenceBits;
/** 数据中心节点较机器标志的偏移量:17位 */
private static long dataCenterIdShift = sequenceBits + workerIdBits;
/** 时间戳较数据中心的偏移量:22位 */
private long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
/** 能存储的最大正整数:4095 */
private static long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 默认内部实例 */
private static final UidGenerator uidGenerator;
/**
* 构造函数
*
* @param port 服务启动端口
* @return UidGenerator
*/
public static UidGenerator newInstance(long port) {
return new UidGenerator(getWorkId(), toNodeId(port));
}
/**
* 构造函数
*
* @param workerId 机器节点,取值范围 [0~31]
* @param dataCenterId 数据中心节点,取值范围 [0~31]
* @return UidGenerator
*/
public static UidGenerator newInstance(long workerId, long dataCenterId) {
return new UidGenerator(workerId, dataCenterId);
}
public static Long generateId() {
return uidGenerator.nextId();
}
private UidGenerator(long workerId, long dataCenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("Worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException(
String.format("DataCenter Id can't be greater than %d or less than 0", maxDataCenterId));
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
public synchronized long nextId() {
// 获取当前毫秒数
long timestamp = getTimeMillis();
// 如果当前时间戳小于上次时间戳则抛出异常。
if (timestamp < lastTimestamp) {
throw new IllegalArgumentException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
// 如果上次生成时间和当前时间相同,在同一毫秒内
if (lastTimestamp == timestamp) {
// sequence 自增,因为 sequence 只有 12bit,所以和 sequenceMask 相与一下,去掉高位
sequence = (sequence + 1) & sequenceMask;
// 判断是否溢出,也就是每毫秒内超过 4095,当为 4096 时,与 sequenceMask 相与,sequence 就等于 0
if (sequence == 0) {
// 自旋等待到下一毫秒
timestamp = tilNextTimeMillis(lastTimestamp);
}
} else {
// 如果和上次生成时间不同,重置 sequence,就是下一毫秒开始,sequence 计数重新从 0 开始累加
sequence = 0L;
}
// 当前时间戳存档记录,用于下次产生 id 时对比是否为相同时间戳
lastTimestamp = timestamp;
// 时间戳部分 | 数据中心部分 | 机器节点部分 | 序列号部分
return ((timestamp - twepoch) << timestampLeftShift)
| (dataCenterId << dataCenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
protected long tilNextTimeMillis(long lastTimestamp) {
long timestamp = getTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = getTimeMillis();
}
return timestamp;
}
protected long getTimeMillis() {
return System.currentTimeMillis();
}
private static long toNodeId(String value) {
int[] ints = StringUtils.toCodePoints(value);
int sums = 0;
for (int b : ints) {
sums += b;
}
return toNodeId(sums);
}
private static long toNodeId(long value) {
return value % 32;
}
private static long getWorkId() {
try {
Set set = NetworkUtils.getInetAddress();
String hostAddress = set.stream().findFirst().map(InetAddress::getHostAddress)
.orElseThrow(NoSuchElementException::new);
return toNodeId(hostAddress);
} catch (NoSuchElementException e) {
logger.warn("Get workId failed then use random id", e);
return RandomUtils.nextLong(0, maxWorkerId);
}
}
private static long getDataCenterId() {
try {
String hostName = Optional.ofNullable(SystemUtils.getHostName())
.orElse(InetAddress.getLocalHost().getHostName());
return toNodeId(hostName);
} catch (UnknownHostException e) {
logger.warn("Get dataCenterId failed then use random id", e);
return RandomUtils.nextLong(0, maxDataCenterId);
}
}
static {
uidGenerator = UidGenerator.newInstance(getWorkId(), getDataCenterId());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy