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; } }