簡體   English   中英

12位唯一分布式隨機數發生器

[英]12 digit unique distributed random number generator

我將sonyflake移植到 Java 並且效果很好。 但是,我希望生成 12 位唯一數字,而不是生成 8 位數字。 原始端口使用 16 位machineId 因為我們至少有 2 個數據中心,但不限於,我為數據中心添加了 8 位 - 使用 IP 地址的第二個八位字節。 我調整了位長的所有設置,無法生成 12 位數字。 是否有受 sonyflake 或 Twitters Snowflake啟發的算法來生成使用 16 位machineId和 8 位dataCenterId的唯一 12 位數字?

注意:由於公司政策,我不能在這里發布我原來的 Java 端口。

編輯:這就是我想出的。 但是,它不會生成 12 位十進制數字,而是生成 10 位或 11 位數字。 我可以對其進行哪些更改以使其始終返回 12 位十進制數? 我知道我需要更改sequence並重新計算時間。 但是,我目前想專注於生成 12 位十進制數。

public class Demo {

    /// 17 time + 4 dc + 10 machine + 8 sequence

    private static final int BIT_LEN_TIME = 17;
    private static final long BIT_WORKER_ID = 10L;
    private static final long BIT_LEN_DC = 4L;
    private static final long BIT_LEN_SEQUENCE = 8L;

    private static final int MAX_WORKER_ID = (int) (Math.pow(2, BIT_WORKER_ID) - 1);
    private static final long MAX_SEQUENCE = (int) (Math.pow(2, BIT_LEN_SEQUENCE) - 1);

    private static final double FLAKE_TIME_UNIT = 1e7; // nsec, i.e. 10 msec
    private static final double LEN_LIMIT = 1e11;
    private static final int START_SEQ = 0;

    private final ReentrantLock mutex = new ReentrantLock();

    private final Instant startInstant;
    private final long startTime;
    private final long dc;
    private long sequence;
    private long lastElapsedTime;
    private long worker;

    public Demo(Instant startInstant) {
        Objects.requireNonNull(startInstant, "startInstant cannot be null");
        if (startInstant.isBefore(Instant.EPOCH) || startInstant.isAfter(Instant.now())) {
            throw new EverestFlakeException("Base time should be after UNIX EPOCH, or before current time.");
        }

        this.startInstant = startInstant;
        this.startTime = this.toEverestFlakeTime(startInstant);
        this.sequence = START_SEQ;
        this.dc = this.msb(this.getDcId()); // 4 bits at most
        this.worker = this.workerId() & ((1 << BIT_WORKER_ID) - 1); // 10 bits at most
    }

    public long next() {
        long currentElapsedTime = this.currentElapsedTime(this.startTime);

        mutex.lock();
        long time = currentElapsedTime & ((1 << BIT_LEN_TIME) - 1); // 17 bits at most
        if (this.sequence == MAX_SEQUENCE) {
            this.sequence = START_SEQ;
            System.out.println("time = " + time);
            sleepMicro(currentElapsedTime - this.lastElapsedTime);
            time = this.currentElapsedTime(this.startTime) & ((1 << BIT_LEN_TIME) - 1);
            System.out.println("time = " + time);
        } else {
            // less than 15000
            if((currentElapsedTime - this.lastElapsedTime) < 0x3a98) {
                sleepMicro(currentElapsedTime - this.lastElapsedTime);
                time = this.currentElapsedTime(this.startTime) & ((1 << BIT_LEN_TIME) - 1);
            }
            this.sequence += (START_SEQ + 1) & MAX_SEQUENCE;
        }

        long id = (time << BIT_LEN_TIME) |
                (worker << BIT_WORKER_ID) |
                (dc << BIT_LEN_DC) |
                (sequence << BIT_LEN_SEQUENCE);
        id += LEN_LIMIT;
        this.lastElapsedTime = currentElapsedTime;
        mutex.unlock();

        return id;
    }

    private void sleepNano(long sleepTime) {
        try {
            System.out.println("nano sleeping for: " + sleepTime);
            TimeUnit.NANOSECONDS.sleep(sleepTime);
        } catch (Exception e) {
            //
        }
    }

    private void sleepMicro(long sleepTime) {
        try {
            System.out.println("micro sleeping for: " + sleepTime);
            TimeUnit.MICROSECONDS.sleep(sleepTime/100);
        } catch (Exception e) {
            //
        }
    }

    private long toEverestFlakeTime(Instant startInstant) {
        return unixNano(startInstant);
    }

    private long unixNano(Instant startInstant) {
        return NanoClock.systemUTC().nanos(startInstant);
    }

    private long currentElapsedTime(long startTime) {
        return this.toEverestFlakeTime(NanoClock.systemUTC().instant()) - startTime;
    }

    private long msb(long n) {
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        n >>>= 1;
        n += 1;
        return n;
    }

    private int workerId() {
        return new SecureRandom().nextInt(MAX_WORKER_ID);
    }

    private int getDcId() {
        try {
            Socket socket = new Socket();
            socket.connect(new InetSocketAddress("google.com", 80));
            byte[] a = socket.getLocalAddress().getAddress();
            socket.close();
            return Byte.toUnsignedInt(a[1]);
        } catch (Exception e) {
            String message = "Failed to process machine id.";
            throw new EverestFlakeException(message, e);
        }
    }
}

如果您的意思是 12 位十進制數字,那么您可以使用最多 39 位的數字(40 位可以表示 12 位數字之外的 13 位數字)。

如果您為機器 ID 取 16 位,為數據中心 ID 取 8 位,那么該機器的唯一 ID 部分只剩下 15 位(因此每台機器只有 32768 個唯一編號)。可以選擇順序分配數字而不是隨機分配。

如果您的意思是 12 個十六進制(base-16)數字,那么情況會大大改善:16 位組成 4 個數字,8 位組成另外兩個,留下 6 個 base-16 數字作為 ID 的唯一部分,或 16,777,216 個不同的數字(24 位)。 有了這么多號碼,您有多種不同的選擇讓每台機器分配這些號碼。 您可以按順序或隨機進行(使用java.security.SecureRandom ,或使用分辨率為 10 毫秒的時間戳,如java.util.Random中那樣)。


看來您的問題與其說是關於如何生成 12 位唯一 ID,不如說是關於如何格式化數字以恰好適合 12 位數字。 那么你有兩個選擇。 假設您有一個 39 位 integer x (小於 2 39因此小於 10 12 )。

  • 如果您可以接受數字中的前導零,則執行以下操作將x格式化為 12 位數字: String.format("%012d", x)

  • 如果您不能接受數字中的前導零,則將 100000000000 (10 11 ) 添加到x 由於x小於 2 39 ,即小於 900000000000,這將產生一個 12 位數字。


您正在隨機生成工作人員 ID。 一般來說,隨機數本身不足以確保唯一性。 您需要某種方法來檢查您生成的每個工作人員 ID 的唯一性。 這樣做后,每個工作人員/數據中心對都將是唯一的。 因此,每台機器只需要生成一個機器唯一編號,在您的情況下將是 25 位長。 有幾種方法可以做到這一點:

  • 最簡單的方法是生成一個隨機數或基於時間的數字(在您的示例中使用所有 25 位)並使用 hash 集(例如java.util.HashSet )檢查數字的唯一性。 如果號碼已經生成,請使用新號碼重試。 (而不是 hash 表,使用位集(例如, java.util.BitSet )或壓縮的 bitmap (例如)“ro”可能更節省內存。

  • 另一種方法是使用 hash 表,其中隨機/基於時間的數字作為鍵,序列 ID 作為值。 當需要生成唯一編號時,請執行以下操作:

    1. 生成X ,一個隨機數或基於時間的數字。
    2. 檢查hash表中是否找到等於X的鍵。
    3. 如果key不存在,則在表中新建一個entry,key為X ,value為0。唯一編號為X ,序列號為0。停止。
    4. 如果鍵存在,並且與該鍵關聯的值小於 255,則將該值加 1。 唯一編號是X和該值的序列號。 停止。
    5. 如果鍵存在,並且與該鍵關聯的值是 255 或更大,則 go 到步驟 1。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM