1. 程式人生 > >從零到日誌採集索引視覺化、監控報警、rpc trace跟蹤-分散式唯一ID生成

從零到日誌採集索引視覺化、監控報警、rpc trace跟蹤-分散式唯一ID生成

public class UniqueIdGen implements IdGen {

    // 開始使用該演算法的時間為: 2017-01-01 00:00:00
private static final long START_TIME 1483200000000L;
// 時間戳bit數,最多能支援到2050年,首位為標記位(javalong首位是0表示為正數)
private static final int TIME_BITS 40;
// worker idbit數,最多支援8192apphost的組合(即在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 id65535
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;
}

    /**
     * 利用twittersnowflake(做了些微修改)演算法來實現
@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;
}

}