1. 程式人生 > 實用技巧 >雪花演算法生成分散式ID(java)

雪花演算法生成分散式ID(java)

/**

  • 雪花演算法生成分散式ID

  • /// 共64位 第1位不使用,242位為時間戳,4352位為工作機器id,53~64 位為序列號

  • /// 可部署1024個節點,每毫秒可產生4096個唯一id

  • 參考連結:https://blog.csdn.net/yangding_/article/details/52768906
    */
    public class IdWorker {

    /// 工作程序id 5位
    private long workerId;
    /// 資料中心id 5位
    private long datacenterId;
    /// 順序 12位,0~4095
    private long sequence = 0L;
    // 初始時間戳
    // 1288834974657 是 (Thu, 04 Nov 2010 01:42:54 GMT) 這一時刻到1970-01-01 00:00:00時刻所經過的毫秒數。
    // 41位位元組作為時間戳數值的話,大約68年就會用完,
    // 假如你2010年1月1日開始開發系統,如果不減去2010年1月1日的時間戳,那麼白白浪費40年的時間戳啊!
    // 所以減去twepoch 可以讓系統在41位位元組作為時間戳的情況下的執行時間更長。1288834974657L可能就是該專案開始成立的時間。
    private long twepoch = 1288834974657L;
    //長度定義及最大值定義
    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private long sequenceBits = 12L;

    // 工作id 左移12位
    private long workerIdShift = sequenceBits;
    //資料中心id 左移17位
    private long datacenterIdShift = sequenceBits + workerIdBits;
    //時間戳左移22位
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    //判斷是否已經到達最大序列號
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public IdWorker(long workerId, long datacenterId) {
    // sanity check for workerId
    if (workerId > maxWorkerId || workerId < 0) {
    throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
    }
    if (datacenterId > maxDatacenterId || datacenterId < 0) {
    throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
    }
    this.workerId = workerId;
    this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
    long timestamp = timeGen();

     //如果當前時間戳<上次時間戳,則是時間回撥情況,丟擲異常
     if (timestamp < lastTimestamp) {
         throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
     }
     //如果是在同一毫秒內,則累加序列號
     if (lastTimestamp == timestamp) {
         sequence = (sequence + 1) & sequenceMask;
         //如果達到最大值,則等待下一毫秒,序列號從0 開始
         if (sequence == 0) {
             timestamp = tilNextMillis(lastTimestamp);
         }
     } else {
         sequence = 0L;
     }
     //重置上一次毫秒數
     lastTimestamp = timestamp;
     //返回時間戳+工作節點+序列號
     return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    

    }

    /// 獲取下一毫秒
    protected long tilNextMillis(long lastTimestamp) {
    long timestamp = timeGen();
    while (timestamp <= lastTimestamp) {
    timestamp = timeGen();
    }
    return timestamp;
    }

    /// 獲取當前時間戳
    protected long timeGen() {
    return System.currentTimeMillis();
    }
    }