net.lulihu.ObjectKit.IDGeneratorKit Maven / Gradle / Ivy
package net.lulihu.ObjectKit;
/**
* 使用SnowFlake算法生成id的结果是一个18位的整数
*/
public class IDGeneratorKit {
private final static IdGenerator generator = IdGeneratorEnum.instance.getInstance();
private IDGeneratorKit() {
// 私有化构造函数
}
/**
* 获取id
*/
public static Long get() {
return generator.nextId();
}
/**
* 获取id
*/
public static String getStr() {
return String.valueOf(generator.nextId());
}
private static class IdGenerator {
private long workerId;
private long datacenterId;
private long sequence = 0;
private IdGenerator() {
final ApparatusUtils apparatusUtils = new ApparatusUtils();
final long[] apparatusId = apparatusUtils.getApparatusId();
long workerId = apparatusId[0];
maxWorkerId = ~(-1L << workerIdBits);
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id 不能大于 %d 或者小于 0", maxWorkerId));
}
long datacenterId = apparatusId[1];
long maxDatacenterId = ~(-1L << datacenterIdBits);
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id 不能大于 %d 或者小于 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
twepoch = 1288834974657L;
}
private long twepoch;
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
long maxWorkerId;
private long sequenceBits = 12L;
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = ~(-1L << sequenceBits);
private long lastTimestamp = -1L;
private synchronized long nextId() {
long timestamp = timeGen();
// 生成的时间戳比上次的时间戳还小,出错
if (timestamp < lastTimestamp) {
throw new RuntimeException(StrKit.format("时钟倒退。 拒绝为 {} 毫秒生成ID", lastTimestamp - timestamp));
}
// 生成的时间戳跟上次的时间戳一样,则需要生成一个sequence序列号
if (lastTimestamp == timestamp) {
// sequence循环自增
sequence = (sequence + 1) & sequenceMask;
// 如果sequence=0则需要重新生成时间戳
if (sequence == 0) {
// 且必须保证时间戳序列往后
timestamp = tilNextMillis(lastTimestamp);
}
// 如果 timestamp > lastTimestamp,时间戳序列已经不同了,此时可以不必生成sequence了,直接取0
} else {
sequence = 0;
}
// 更新lastTimestamp时间戳
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
private static class ApparatusUtils {
private final String[] binaryArray = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
/**
* 获取机器id
* 分为 workerId或datecenterId
*/
private long[] getApparatusId() {
// cpu 16进制 序列号
final String processorId = NativeKit.CPUSerialNumber();
// 装换成 2进制字符码
final StringBuilder builder = bytes2BitStr(hexStringToByte(processorId));
// 取前10位 换算成 workerId datacenterId
long workerId = Integer.valueOf(builder.substring(0, 4), 2);
long datacenterId = Integer.valueOf(builder.substring(5, 9), 2);
return new long[]{workerId, datacenterId};
}
/**
* 十六进制转二进制
*/
private byte[] hexStringToByte(String hexString) {
//hexString的长度对2取整,作为bytes的长度
int len = hexString.length() / 2;
byte[] bytes = new byte[len];
byte high;//字节高四位
byte low;//字节低四位
for (int i = 0; i < len; i++) {
//右移四位得到高位
String hexStr = "0123456789ABCDEF";
high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4);
low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1));
bytes[i] = (byte) (high | low);//高地位做或运算
}
return bytes;
}
/**
* 二进制数组转换为二进制字符串
*/
private StringBuilder bytes2BitStr(byte[] bArray) {
StringBuilder builder = new StringBuilder();
int pos;
for (byte b : bArray) {
//高四位
pos = (b & 0xF0) >> 4;
builder.append(binaryArray[pos]);
//低四位
pos = b & 0x0F;
builder.append(binaryArray[pos]);
}
return builder;
}
}
/**
* 枚举式单例
*/
public enum IdGeneratorEnum {
instance;
IdGeneratorEnum() {
generator = new IdGenerator();
}
private IdGenerator generator;
public IdGenerator getInstance() {
return generator;
}
}
}