All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wl4g.infra.common.id.SnowflakeIdGenerator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 ~ 2025 the original author or authors. James Wong 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wl4g.infra.common.id;

import static com.wl4g.infra.common.lang.Assert2.isTrueOf;
import static com.wl4g.infra.common.lang.Assert2.notNullOf;
import static java.lang.String.format;

import java.util.concurrent.atomic.AtomicLong;

/**
 * Snowflake algorithms Id generator.
 *
 * @author James Wong 
 * @version v1.0 2018年6月10日
 * @since
 * @see https://github.com/twitter/snowflake
 */
public class SnowflakeIdGenerator {

    /**
     * {@link BitsDefine}
     */
    private final BitsDefine def;
    // Worker node ID.
    private final long workerId;
    // Data center ID.
    private final long dataCenterId;
    // Sequence ID.
    private final AtomicLong sequence = new AtomicLong(0L);
    // Last generate timestamp.
    private final AtomicLong lastTime = new AtomicLong(-1L);

    public SnowflakeIdGenerator() {
        this(BitsDefine.StandardSafeJs, 0L, 0L, 0L);
    }

    public SnowflakeIdGenerator(BitsDefine def, long workerId, long dataCenterId, long sequence) {
        validate(def, workerId, dataCenterId, sequence);
        this.def = def;
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
        this.sequence.set(sequence);
    }

    /**
     * Gets next global UID. 
* * @return */ public synchronized long nextId() { long now = timeGen(); // 如果服务器时间有问题(时钟后退) 报错。 if (now < lastTime.get()) { throw new IllegalStateException( format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTime.get() - now)); } // 如果上次生成时间和当前时间相同,在同一毫秒内 if (lastTime.get() == now) { // sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位 sequence.set(sequence.incrementAndGet() & def.sequenceMask); // 判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0 if (sequence.get() == 0) { // 自旋等待到下一毫秒 now = tilNextMillis(lastTime.get()); } } else { // 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加 sequence.set(0L); } lastTime.set(now); // Finally, the ID is calculated according to the rules. // 000000000000000000000000000000000000000000 00000 00000 000000000000 // time dataCenterId workerId sequence return ((now - def.twepoch) << def.timestampLeftShift) | (dataCenterId << def.dataCenterIdShift) | (workerId << def.workerIdShift) | sequence.get(); } /** * Spin wait until next MS * * @param lastTimestamp * @return */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * Gets now timestamp. * * @return */ protected long timeGen() { return System.currentTimeMillis(); } /** * Validation bits definition options. * * @param def * @param workerId * @param dataCenterId * @param sequence */ private void validate(BitsDefine def, long workerId, long dataCenterId, long sequence) { notNullOf(def, "bitsDefine"); isTrueOf(workerId >= 0, "workerId>=0"); isTrueOf(dataCenterId >= 0, "dataCenterId>=0"); isTrueOf(sequence >= 0, "sequence>=0"); isTrueOf(def.sequenceBits >= 0, "sequenceBits>=0"); if (workerId > def.maxWorkerId || workerId < 0) { throw new IllegalArgumentException(format("worker Id can't be greater than %d or less than 0", def.maxWorkerId)); } if (dataCenterId > def.maxDatacenterId || dataCenterId < 0) { throw new IllegalArgumentException( format("datacenter Id can't be greater than %d or less than 0", def.maxDatacenterId)); } } /** * Gets {@link SnowflakeIdGenerator} default instance. * * @return */ public final static SnowflakeIdGenerator getDefault() { return SingletionHolder.instance; } /** * {@link SingletionHolder} singletion holder * * @author James Wong * @version v1.0 2020年6月10日 * @since */ private static final class SingletionHolder { private static final SnowflakeIdGenerator instance = new SnowflakeIdGenerator(); } /** * {@link BitsDefine} * * @author James Wong * @version v1.0 2020-10-09 * @since */ public static final class BitsDefine { // Epoch (e.g: 1288834974657L => Thu, 04 Nov 2010 01:42:54 GMT) final long twepoch; // Node ID length bits. final long workerIdBits; // Data Center ID length bits. final long dataCenterIdBits; // The maximum number of machine nodes supported is 0-31, a total of 32 final long maxWorkerId; // The maximum number of data center nodes supported is 0-31, a total of // 32 final long maxDatacenterId; // Serial number length bits. final long sequenceBits; // Machine node left shift length bits. final long workerIdShift; // Data center node moves length bits. final long dataCenterIdShift; // Time milliseconds shift length bits. final long timestampLeftShift; // Sequence mask bits. final long sequenceMask; public BitsDefine(long twepoch, long workerIdBits, long dataCenterIdBits, long sequenceBits) { this.twepoch = twepoch; this.workerIdBits = workerIdBits; this.dataCenterIdBits = dataCenterIdBits; // The maximum number of machine nodes supported is 0-31, a total of // 32 this.maxWorkerId = -1L ^ (-1L << workerIdBits); // The maximum number of data center nodes supported is 0-31, a // total of 32 this.maxDatacenterId = -1L ^ (-1L << dataCenterIdBits); // Serial number. (e.g: 12 digits) this.sequenceBits = sequenceBits; // Machine node left shift bits. (e.g: 12 bits) this.workerIdShift = sequenceBits; // Data center node moves left bits. (e.g: 17 bits) this.dataCenterIdShift = sequenceBits + workerIdBits; // Time milliseconds shift left bits. (e.g: 22 bits) this.timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits; // Sequence mask bits. (e.g: 4095 bits). this.sequenceMask = -1L ^ (-1L << sequenceBits); // Validation isTrueOf(twepoch >= 0, "twepoch>=0"); isTrueOf(workerIdBits >= 0, "workerIdBits>=0"); isTrueOf(dataCenterIdBits >= 0, "dataCenterIdBits>=0"); isTrueOf(sequenceBits >= 0, "sequenceBits>=0"); } /** * Generates a decimal integer with a maximum length of 16, which is * JavaScript compatible (i.e., IEEE 754 specification). * *
         * twepoch=1288834974657L => Thu, 04 Nov 2010 01:42:54 GMT
         * 
*/ public static final BitsDefine StandardSafeJs = new BitsDefine(1288834974657L, 2L, 2L, 10L); /** * Generates a decimal integer with a maximum length of 19. Note: * that it is not compatible with JavaScript (does not follow the IEEE * 754 specification), and is suitable for high concurrency and non web * browser systems. The system of web browser recommends using the safe * option: {@link #StandardSafeJs}. * *
         * twepoch=1288834974657L => Thu, 04 Nov 2010 01:42:54 GMT
         * 
*/ public static final BitsDefine LargeUnsafeJs = new BitsDefine(1288834974657L, 5L, 5L, 12L); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy