package com.jthink.skyeye.trace.generater;
/**
* JThink@JThink
*
* @author JThink
* @version 0.0.1
* @desc 分布式唯一ID生成器,用来生成traceID和spanID
* @date 2017-03-24 11:25:31
*/
public class UniqueIdGen implements IdGen {
// 开始使用该算法的时间为: 2017-01-01 00:00:00
private static final long START_TIME = 1483200000000L;
// 时间戳bit数,最多能支持到2050年,首位为标记位(java的long首位是0表示为正数)
private static final int TIME_BITS = 40;
// worker id的bit数,最多支持8192个app和host的组合(即在N个服务器上每个服务器部署M个项目,总共部署N*M=8192)
private static final int APP_HOST_ID_BITS = 13;
// 序列号,支持单节点最高1000*1024的并发
private final static int SEQUENCE_BITS = 10;
// 最大的app host id,65535
private final static long MAX_APP_HOST_ID = ~(-1L << APP_HOST_ID_BITS);
// 最大的序列号,127
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// app host编号的移位
private final static long APP_HOST_ID_SHIFT = SEQUENCE_BITS;
// 时间戳的移位
private final static long TIMESTAMP_LEFT_SHIFT = APP_HOST_ID_BITS + APP_HOST_ID_SHIFT;
// 该项目的app host id,对应着为某台机器上的某个项目分配的serviceId(注意区分Span中的serviceId)
private long appHostId;
// 上次生成ID的时间戳
private long lastTimestamp = -1L;
// 当前毫秒生成的序列
private long sequence = 0L;
// 单例
private static volatile UniqueIdGen idGen = null;
/**
* 实例化
* @param appHostId
* @return
*/
public static UniqueIdGen getInstance(long appHostId) {
if (idGen == null) {
synchronized(UniqueIdGen.class) {
if (idGen == null) {
idGen = new UniqueIdGen(appHostId);
}
}
}
return idGen;
}
private UniqueIdGen(long appHostId) {
if (appHostId > MAX_APP_HOST_ID) {
// zk分配的serviceId过大(基本小规模的公司不会出现这样的问题)
throw new IllegalArgumentException(String.format("app host Id wrong: %d ", appHostId));
}
this.appHostId = appHostId;
}
/**
* 利用twitter的snowflake(做了些微修改)算法来实现
* @return
*/
@Override
public String nextId() {
return Long.toHexString(this.genUniqueId());
}
/**
* 生成唯一id的具体实现
* @return
*/
private synchronized long genUniqueId() {
long current = System.currentTimeMillis();
if (current < lastTimestamp) {
// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,出现问题返回-1
return -1;
}
if (current == lastTimestamp) {
// 如果当前生成id的时间还是上次的时间,那么对sequence序列号进行+1
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == MAX_SEQUENCE) {
// 当前毫秒生成的序列数已经大于最大值,那么阻塞到下一个毫秒再获取新的时间戳
current = this.nextMs(lastTimestamp);
}
} else {
// 当前的时间戳已经是下一个毫秒
sequence = 0L;
}
// 更新上次生成id的时间戳
lastTimestamp = current;
// 进行移位操作生成int64的唯一ID
return ((current - START_TIME) << TIMESTAMP_LEFT_SHIFT)
| (this.appHostId << APP_HOST_ID_SHIFT)
| sequence;
}
/**
* 阻塞到下一个毫秒
* @param timeStamp
* @return
*/
private long nextMs(long timeStamp) {
long current = System.currentTimeMillis();
while (current <= timeStamp) {
current = System.currentTimeMillis();
}
return current;
}
}