001/*
002 *    Copyright 2024-2025, Warm-Flow (290631660@qq.com).
003 *
004 *    Licensed under the Apache License, Version 2.0 (the "License");
005 *    you may not use this file except in compliance with the License.
006 *    You may obtain a copy of the License at
007 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
010 *    Unless required by applicable law or agreed to in writing, software
011 *    distributed under the License is distributed on an "AS IS" BASIS,
012 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *    See the License for the specific language governing permissions and
014 *    limitations under the License.
015 */
016package org.dromara.warm.flow.core.keygen;
017
018/**
019 * warm 生成14位有序id
020 *
021 * @author warm
022 */
023public class SnowFlakeId14 implements KenGen {
024    /**
025     * 开始时间截 (本次时间戳为:Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间)----1288834974657L---1656543015264587776--19 )
026     */
027    private final long startTime = 1683803335498L;
028
029    /**
030     * 机器id所占的位数
031     */
032    private final long workerIdBits = 3L;
033
034    /**
035     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
036     */
037    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
038
039    /**
040     * 序列在id中占的位数
041     */
042    private final long sequenceBits = 5L;
043
044    /**
045     * 机器ID向左移12位
046     */
047    private final long workerIdShift = sequenceBits;
048
049    /**
050     * 时间截向左移22位(10+12)
051     */
052    private final long timestampLeftShift = sequenceBits + workerIdBits;
053
054    /**
055     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
056     */
057    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
058
059    /**
060     * 工作机器ID(0~1024)
061     */
062    private long workerId;
063
064    /**
065     * 毫秒内序列(0~4095)
066     */
067    private long sequence = 0L;
068
069    /**
070     * 上次生成ID的时间截
071     */
072    private long lastTimestamp = -1L;
073
074    //==============================Constructors=====================================
075
076    /**
077     * 构造函数
078     *
079     * @param workerId 工作ID (0~1024)
080     */
081    public SnowFlakeId14(long workerId) {
082        if (workerId > maxWorkerId || workerId < 0) {
083            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
084        }
085        this.workerId = workerId;
086    }
087
088    // ==============================Methods==========================================
089
090    /**
091     * 获得下一个ID (该方法是线程安全的)
092     *
093     * @return SnowflakeId
094     */
095    @Override
096    public synchronized long nextId() {
097        long timestamp = timeGen();
098
099        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
100        if (timestamp < lastTimestamp) {
101            throw new RuntimeException(
102                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
103        }
104
105        //如果是同一时间生成的,则进行毫秒内序列
106        if (lastTimestamp == timestamp) {
107            sequence = (sequence + 1) & sequenceMask;
108            //毫秒内序列溢出
109            if (sequence == 0) {
110                //阻塞到下一个毫秒,获得新的时间戳
111                timestamp = tilNextMillis(lastTimestamp);
112            }
113        }
114        //时间戳改变,毫秒内序列重置
115        else {
116            sequence = 0L;
117        }
118
119        //上次生成ID的时间截
120        lastTimestamp = timestamp;
121
122        //移位并通过或运算拼到一起组成64位的ID
123        return ((timestamp - startTime) << timestampLeftShift)
124                | (workerId << workerIdShift)
125                | sequence;
126    }
127
128    /**
129     * 阻塞到下一个毫秒,直到获得新的时间戳
130     *
131     * @param lastTimestamp 上次生成ID的时间截
132     * @return 当前时间戳
133     */
134    protected long tilNextMillis(long lastTimestamp) {
135        long timestamp = timeGen();
136        while (timestamp <= lastTimestamp) {
137            timestamp = timeGen();
138        }
139        return timestamp;
140    }
141
142    /**
143     * 返回以毫秒为单位的当前时间
144     *
145     * @return 当前时间(毫秒)
146     */
147    protected long timeGen() {
148        return System.currentTimeMillis();
149    }
150
151}