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

com.github.kaizen4j.id.UidGenerator Maven / Gradle / Ivy

package com.github.kaizen4j.id;

import com.github.kaizen4j.util.NetworkUtils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Twitter SnowFlake for Java implements
 *
 * 推荐以单例模式持有实例
 *
 * @author liuguowen
 */
public class UidGenerator {

    private static final Logger logger = LoggerFactory.getLogger(UidGenerator.class);

    /** 机器节点 Id */
    private long workerId;

    /** 数据中心节点 Id */
    private long dataCenterId;

    /** 序列号 */
    private long sequence = 0L;

    /** 上一次时间戳 */
    private long lastTimestamp = -1L;

    /** 起始的时间戳:2021-07-01 12:21:32 */
    private static long twepoch = 1625113292000L;

    /** 机器节点占用的位数 */
    private static long workerIdBits = 5L;

    /** 数据中心占用的位数 */
    private static long dataCenterIdBits = 5L;

    /** 最大支持机器节点数 0~31,一共 32 个 */
    private static long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 最大支持数据中心节点数 0~31,一共 32 个 */
    private static long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    /** 序列号占用的位数 */
    private static long sequenceBits = 12L;

    /** 机器节点较序列号的偏移量:12位 */
    private static long workerIdShift = sequenceBits;

    /** 数据中心节点较机器标志的偏移量:17位 */
    private static long dataCenterIdShift = sequenceBits + workerIdBits;

    /** 时间戳较数据中心的偏移量:22位 */
    private long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    /** 能存储的最大正整数:4095 */
    private static long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 默认内部实例 */
    private static final UidGenerator uidGenerator;

    /**
     * 构造函数
     *
     * @param port 服务启动端口
     * @return UidGenerator
     */
    public static UidGenerator newInstance(long port) {
        return new UidGenerator(getWorkId(), toNodeId(port));
    }

    /**
     * 构造函数
     *
     * @param workerId 机器节点,取值范围 [0~31]
     * @param dataCenterId 数据中心节点,取值范围 [0~31]
     * @return UidGenerator
     */
    public static UidGenerator newInstance(long workerId, long dataCenterId) {
        return new UidGenerator(workerId, dataCenterId);
    }

    public static Long generateId() {
        return uidGenerator.nextId();
    }

    private UidGenerator(long workerId, long dataCenterId) {
        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 = getTimeMillis();
        // 如果当前时间戳小于上次时间戳则抛出异常。
        if (timestamp < lastTimestamp) {
            throw new IllegalArgumentException(
                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
                            lastTimestamp - timestamp));
        }

        // 如果上次生成时间和当前时间相同,在同一毫秒内
        if (lastTimestamp == timestamp) {
            // sequence 自增,因为 sequence 只有 12bit,所以和 sequenceMask 相与一下,去掉高位
            sequence = (sequence + 1) & sequenceMask;
            // 判断是否溢出,也就是每毫秒内超过 4095,当为 4096 时,与 sequenceMask 相与,sequence 就等于 0
            if (sequence == 0) {
                // 自旋等待到下一毫秒
                timestamp = tilNextTimeMillis(lastTimestamp);
            }
        } else {
            // 如果和上次生成时间不同,重置 sequence,就是下一毫秒开始,sequence 计数重新从 0 开始累加
            sequence = 0L;
        }

        // 当前时间戳存档记录,用于下次产生 id 时对比是否为相同时间戳
        lastTimestamp = timestamp;

        // 时间戳部分 | 数据中心部分 | 机器节点部分 | 序列号部分
        return ((timestamp - twepoch) << timestampLeftShift)
                | (dataCenterId << dataCenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    protected long tilNextTimeMillis(long lastTimestamp) {
        long timestamp = getTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = getTimeMillis();
        }
        return timestamp;
    }

    protected long getTimeMillis() {
        return System.currentTimeMillis();
    }

    private static long toNodeId(String value) {
        int[] ints = StringUtils.toCodePoints(value);
        int sums = 0;
        for (int b : ints) {
            sums += b;
        }
        return toNodeId(sums);
    }

    private static long toNodeId(long value) {
        return value % 32;
    }

    private static long getWorkId() {
        try {
            Set set = NetworkUtils.getInetAddress();
            String hostAddress = set.stream().findFirst().map(InetAddress::getHostAddress)
                    .orElseThrow(NoSuchElementException::new);
            return toNodeId(hostAddress);
        } catch (NoSuchElementException e) {
            logger.warn("Get workId failed then use random id", e);
            return RandomUtils.nextLong(0, maxWorkerId);
        }
    }

    private static long getDataCenterId() {
        try {
            String hostName = Optional.ofNullable(SystemUtils.getHostName())
                    .orElse(InetAddress.getLocalHost().getHostName());
            return toNodeId(hostName);
        } catch (UnknownHostException e) {
            logger.warn("Get dataCenterId failed then use random id", e);
            return RandomUtils.nextLong(0, maxDataCenterId);
        }
    }

    static {
        uidGenerator = UidGenerator.newInstance(getWorkId(), getDataCenterId());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy