分佈式Id:Snowflake簡單實現

Twitter SnowFlake

說明

第1位:不使用
第[2,42]:毫秒級時間。可使用69年
第[43,47]:datacenterId
第[48,52]:workerId
[43,52]十位最多部署1024個節點
第[53,64]:毫秒內的計數/序列號

Long的值

0, 時間截=當前-開始, 數據中心Id+機器Id,12位序列號

代碼

public class Snowflake {
    /*
    第1位:不使用
    第[2,42]:毫秒級時間。可使用69年
    第[43,47]:dataCenterId
    第[48,52]:workerId
        [43,52]十位最多部署1024個節點
    第[53,64]:毫秒內的計數/序列號

    1. 開始時間戳
    2. 所佔位數:時間截、數據中心、機器、序列號
    3. 最大值:時間截、數據中心、機器、序列號
    4. 移位:
    5. 存儲位:時間截,機器Id,數據中心Id,序列號Id
     */
    // 1. 開始時間戳
    private final long epoch = System.currentTimeMillis();
    // 2. 所佔位數
    private final long[] bits = {41L, 5L, 5L, 12L};
    // 3. 最大值
    private final long[] max = {-1L ^ (-1L << bits[0]), -1L ^ (-1L << bits[1]), -1L ^ (-1L << bits[2]), -1L ^ (-1L << bits[3])};
    // 4. 移位
    private final long[] move = {bits[1] + bits[2] + bits[3], bits[2] + bits[3], bits[3]};
    // 5. 對應域的當前值
    private long timestamp = -1L;
    private long dataCenterId;
    private long workerId;
    private long sequence = 0L;

    /**
     * 0表示時間截,1表示數據中心,2表示機器Id,3表示序列號。使用枚舉量比較好,此處省事
     * @param index
     * @param cur
     * @return
     */
    private boolean checkRange(int index, long cur) {
        if (cur < 0 || cur > max[index]) {
            return true;
        }
        return false;
    }

    public Snowflake(long dataCenterId, long workerId) {
        if (checkRange(1, dataCenterId)) {
            String info = String.format("數據中心Id的範圍應爲:[0, {}]",  max[1]);
            throw new IllegalArgumentException(info + ";當前爲:" + dataCenterId);
        }
        if (checkRange(2, workerId)) {
            String info = String.format("機器Id的範圍應爲:[0, {}]",  max[2]);
            throw new IllegalArgumentException(info + ";當前爲:" + workerId);
        }
        this.dataCenterId = dataCenterId;
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < this.timestamp) {
            // 當前時間 < 上一次時間。系統時鐘回退,應拋出異常
            String info = String.format("系統時鐘回退,拒絕生成時間截:{},上一次時間截:{}", timestamp - epoch, this.timestamp - epoch);
            throw new RuntimeException(info);
        } else if (timestamp == this.timestamp) {
            // 同一毫秒內生成,則序列號升序
            sequence = (sequence + 1) % max[3];
            if (sequence == 0) {
                System.out.println("max:" + max[3]);
                // 序列溢出,比如同一毫秒內生成id過多,超出序列號最大值12的4096-1=4095
                timestamp = nextMills(this.timestamp);
            }
        } else {
            sequence = 0L;
        }
        // 更新this.timestamp
        this.timestamp = timestamp;
        return ((timestamp - epoch) << move[0] | dataCenterId << move[1] | workerId << move[2] | sequence);
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }
    protected long nextMills(long last) {
        long timestamp = timeGen();
        while (timestamp <= last) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        Snowflake snowflake = new Snowflake(0, 0);
        for (int i = 0; i < 10000; ++i) {
            long id = snowflake.nextId();
            System.out.println(Long.toBinaryString(id));
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章