/**
* tweeter的snowflake 移植到Java.参考资料:https://github.com/twitter/snowflake
* id构成: 42位的时间前缀 + 10位的节点标识 + 12位的sequence避免并发的数字(12位不够用时强制得到新的时间前缀)
* id单调递增,长整型
* 注意这里进行了小改动: snowkflake是5位的datacenter加5位的机器id; 这里变成使用10位的机器id
* snowkflake当时间调整时将拒绝分配ID,这里改成分配UUID的tMostSignificantBits
* 通过取服务器名中的编号来分配机器id,实现了去中心化【仅适用于包含编号的机器】
* 使用:long id = IdGenerator.nextId();
*/
@Log4j2
public final class IdGenerator {
/**
* 每台机器分配不同的id
*/
private static final long WORKER_ID;
/**
* 时间起始标记点,作为基准,一般取系统的最近时间
*/
private static final long EPOCH_NUM = 1568017964000L;
/**
* 机器标识位数
*/
private static final long WORKER_IDBITS = 10L;
/**
* 机器ID最大值: 1023
*/
private static final long MAXWORKER_ID = -1L ^ -1L << WORKER_IDBITS;
/**
* 毫秒内自增位
*/
private static final long SEQUENCE_BITS = 12L;
/**
* 12
*/
private static final long WORKER_IDSHIFT = SEQUENCE_BITS;
/**
* 22
*/
private static final long TIMESTAMP_LEFTSHIFT = WORKER_IDBITS + SEQUENCE_BITS;
/**
* 4095,111111111111,12位
*/
private static final long SEQUENCE_MASK = -1L ^ -1L << SEQUENCE_BITS;
/**
* 0,并发控制
*/
private static long sequenceSe = 0L;
private static long lastTimestamp = -1L;
static {
String hostName = null;
try {
InetAddress netAddress = InetAddress.getLocalHost();
hostName = netAddress.getHostName();
} catch (UnknownHostException e) {
log.error(e);
}
if (null != hostName && !"".equals(hostName)) {
String hostNo = "";
for (int i = 0; i < hostName.length(); i++) {
if (hostName.charAt(i) > 48 && hostName.charAt(i) <= 57) {
// 取最后一组数字
if ("".equals(hostNo)) {
hostNo += hostName.charAt(i);
} else {
hostNo = "";
hostNo += hostName.charAt(i);
}
}
}
if (!"".equals(hostNo)) {
WORKER_ID = Integer.parseInt(hostNo) % MAXWORKER_ID;
} else {
WORKER_ID = new SecureRandom().nextInt(1000) + 1;
}
} else {
WORKER_ID = new SecureRandom().nextInt(1000) + 1;
}
}
/**
* Next id long.
*
* @return the long
*/
public static synchronized long nextId() {
long timestamp = timeGen();
// 如果上一个timestamp与新产生的相等,则sequence加一(0-4095循环); 对新的timestamp,sequence从0开始
if (lastTimestamp == timestamp) {
sequenceSe = sequenceSe + 1 & SEQUENCE_MASK;
if (sequenceSe == 0) {
// 重新生成timestamp
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequenceSe = 0;
}
if (timestamp < lastTimestamp) {
UUID uuid = UUID.randomUUID();
return uuid.getMostSignificantBits();
}
lastTimestamp = timestamp;
return timestamp - EPOCH_NUM << TIMESTAMP_LEFTSHIFT | WORKER_ID << WORKER_IDSHIFT | sequenceSe;
}
/**
*
*/
/**
* 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
*
* @param lastTimestamp the last Timestamp
* @return the long
*/
private static long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 获得系统当前毫秒数
*
* @return the long
*/
public static long timeGen() {
return System.currentTimeMillis();
}
}