分布式唯一键生成
@Slf4j
public class UniqueKeyUtils {
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMddHHmmssSSS");
private final long numberBitsMast = 26L;
private final long dataCenterIdBits = 3L;
private final long workerIdBits = 11L;
private final long sequenceBits = numberBitsMast - dataCenterIdBits - workerIdBits;
private final long sequenceMask = ~(-1L << sequenceBits);
private final long workerIdShift = sequenceBits;
private final long dataCenterIdShift = workerIdShift + workerIdBits;
private long sequence = 0L;
private final String numberFullLeft = "%08d";
private long lastTimestamp = -1L;
private final long dataCenterId;
private final long workerId;
private UniqueKeyUtils() {
long maxDataCenterId = ~(-1L << dataCenterIdBits);
this.dataCenterId = getDataCenterId(maxDataCenterId);
long maxWorkerId = ~(-1L << workerIdBits);
this.workerId = getMaxWorkerId(dataCenterId, maxWorkerId);
}
public static String generateNo() {
return getSingleton().nextCode();
}
private static UniqueKeyUtils getSingleton() {
return SingletonHolder.SINGLETON;
}
private static final class SingletonHolder {
static final UniqueKeyUtils SINGLETON = new UniqueKeyUtils();
}
private synchronized String nextCode() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
long offset = lastTimestamp - timestamp;
if (offset <= 5) {
try {
wait(offset << 1);
timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = ThreadLocalRandom.current().nextLong(1, 1000);
}
lastTimestamp = timestamp;
return TIME_FORMATTER.format(LocalDateTime.ofInstant(new Date(timestamp).toInstant(), ZoneId.systemDefault()))
+ String.format(numberFullLeft, ((dataCenterId << dataCenterIdShift)
| (workerId << workerIdShift)
| sequence));
}
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 - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
id = id % (maxDataCenterId + 1);
}
}
} catch (Exception e) {
log.info(" getDataCenterId: " + e.getMessage());
}
return id;
}
private static long getMaxWorkerId(long maxDataCenterId, long maxWorkerId) {
StringBuilder mpId = new StringBuilder();
mpId.append(maxDataCenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (null != name && name.trim().length() > 0) {
mpId.append(name.split("@")[0]);
}
return (mpId.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return SystemClock.now();
}
public static void main(String[] args) {
long t = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
System.out.println(generateNo());
}
System.out.println(System.currentTimeMillis() - t);
}
}