package com.xmzs.midjourney.util;
|
|
import cn.hutool.core.exceptions.ValidateException;
|
import com.xmzs.midjourney.exception.SnowFlakeException;
|
import lombok.extern.slf4j.Slf4j;
|
|
import java.lang.management.ManagementFactory;
|
import java.net.InetAddress;
|
import java.net.NetworkInterface;
|
import java.util.Date;
|
import java.util.concurrent.ThreadLocalRandom;
|
|
@Slf4j
|
public class SnowFlake {
|
private long workerId;
|
private long datacenterId;
|
private long sequence = 0L;
|
private final long twepoch;
|
private final long sequenceMask;
|
private final long workerIdShift;
|
private final long datacenterIdShift;
|
private final long timestampLeftShift;
|
private long lastTimestamp = -1L;
|
private final boolean randomSequence;
|
private long count = 0L;
|
private final long timeOffset;
|
private final ThreadLocalRandom tlr = ThreadLocalRandom.current();
|
|
public static final SnowFlake INSTANCE = new SnowFlake();
|
|
private SnowFlake() {
|
this(false, 10, null, 5L, 5L, 12L);
|
}
|
|
private SnowFlake(boolean randomSequence, long timeOffset, Date epochDate, long workerIdBits, long datacenterIdBits, long sequenceBits) {
|
if (null != epochDate) {
|
this.twepoch = epochDate.getTime();
|
} else {
|
// 2012/12/12 23:59:59 GMT
|
this.twepoch = 1355327999000L;
|
}
|
long maxWorkerId = ~(-1L << workerIdBits);
|
long maxDatacenterId = ~(-1L << datacenterIdBits);
|
this.sequenceMask = ~(-1L << sequenceBits);
|
this.workerIdShift = sequenceBits;
|
this.datacenterIdShift = sequenceBits + workerIdBits;
|
this.timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
this.randomSequence = randomSequence;
|
this.timeOffset = timeOffset;
|
try {
|
this.datacenterId = getDatacenterId(maxDatacenterId);
|
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
|
} catch (Exception e) {
|
log.warn("datacenterId or workerId generate error: {}, set default value", e.getMessage());
|
this.datacenterId = 4;
|
this.workerId = 1;
|
}
|
}
|
|
public synchronized String nextId() {
|
long currentTimestamp = timeGen();
|
if (currentTimestamp < this.lastTimestamp) {
|
long offset = this.lastTimestamp - currentTimestamp;
|
if (offset > this.timeOffset) {
|
throw new ValidateException("Clock moved backwards, refusing to generate id for [" + offset + "ms]");
|
}
|
try {
|
this.wait(offset << 1);
|
} catch (InterruptedException e) {
|
throw new SnowFlakeException(e);
|
}
|
currentTimestamp = timeGen();
|
if (currentTimestamp < this.lastTimestamp) {
|
throw new SnowFlakeException("Clock moved backwards, refusing to generate id for [" + offset + "ms]");
|
}
|
}
|
if (this.lastTimestamp == currentTimestamp) {
|
long tempSequence = this.sequence + 1;
|
if (this.randomSequence) {
|
this.sequence = tempSequence & this.sequenceMask;
|
this.count = (this.count + 1) & this.sequenceMask;
|
if (this.count == 0) {
|
currentTimestamp = this.tillNextMillis(this.lastTimestamp);
|
}
|
} else {
|
this.sequence = tempSequence & this.sequenceMask;
|
if (this.sequence == 0) {
|
currentTimestamp = this.tillNextMillis(lastTimestamp);
|
}
|
}
|
} else {
|
this.sequence = this.randomSequence ? this.tlr.nextLong(this.sequenceMask + 1) : 0L;
|
this.count = 0L;
|
}
|
this.lastTimestamp = currentTimestamp;
|
long id = ((currentTimestamp - this.twepoch) << this.timestampLeftShift) |
|
(this.datacenterId << this.datacenterIdShift) |
|
(this.workerId << this.workerIdShift) |
|
this.sequence;
|
return String.valueOf(id);
|
}
|
|
private static long getDatacenterId(long maxDatacenterId) {
|
long id = 0L;
|
try {
|
InetAddress ip = InetAddress.getLocalHost();
|
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
if (network == null) {
|
id = 1L;
|
} else {
|
byte[] mac = network.getHardwareAddress();
|
if (null != mac) {
|
id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
|
id = id % (maxDatacenterId + 1);
|
}
|
}
|
} catch (Exception e) {
|
throw new SnowFlakeException(e);
|
}
|
return id;
|
}
|
|
private static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
|
StringBuilder macIpPid = new StringBuilder();
|
macIpPid.append(datacenterId);
|
try {
|
String name = ManagementFactory.getRuntimeMXBean().getName();
|
if (name != null && !name.isEmpty()) {
|
macIpPid.append(name.split("@")[0]);
|
}
|
String hostIp = InetAddress.getLocalHost().getHostAddress();
|
String ipStr = hostIp.replace("\\.", "");
|
macIpPid.append(ipStr);
|
} catch (Exception e) {
|
throw new SnowFlakeException(e);
|
}
|
return (macIpPid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
|
}
|
|
private long tillNextMillis(long lastTimestamp) {
|
long timestamp = timeGen();
|
while (timestamp <= lastTimestamp) {
|
timestamp = timeGen();
|
}
|
return timestamp;
|
}
|
|
private long timeGen() {
|
return System.currentTimeMillis();
|
}
|
|
}
|