yz
2021-04-09 e775c15fde0024a897608e0ceaaed5c0c3f10850
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package cc.mrbird.febs.common.core.utils;
 
import com.baomidou.mybatisplus.core.toolkit.SystemClock;
import lombok.extern.slf4j.Slf4j;
 
/**
 * name: SequenceUtil
 * package: cc.mrbird.febs.common.core.utils
 * description: 全局ID生成器,基于雪花算法
 * 时间戳+机器码+系统码+序列号
 * 时间戳:从2020-01-01开始的秒数 32位
 * 机器码:5位  可有32台机器
 * 系统码:6位  可设置64个系统
 * 系统框架
 * 系统管理 000000     1
 * 组织架构 000001     2
 * 运营管理000010      3
 * Hr系统
 * 组织规划 001010   10
 * 招聘选拔 001011   11
 * 员工关系 001100   12
 * 考勤管理 001101   13
 * 薪酬管理 001110   14
 * 社保福利 001111   15
 * 绩效管理 010000   16
 * 培训开发 010001   17
 * 序列号:10位,每秒1024个ID
 * date: 2021-01-27 20:39
 *
 * @author luoyibo
 * @version 0.1
 * @since JDK 1.8
 */
@Slf4j
public class SequenceUtil {
    /**
     * 初始偏移时间戳
     */
    private static final long OFFSET = 1546300800L;
 
    /**
     * 机器id
     */
    private static long WORKER_ID;
    /**
     * 系统模块码
     */
    private static long MODULE_CODE;
    /**
     * 机器id所占位数 (5bit, 支持最大机器数 2^5 = 32)
     */
    private static final long WORKER_ID_BITS = 5L;
    /**
     * 系统模块码所占位数 (6bit, 支持最大机器数 2^6 = 64)
     */
    private static final long MODULE_CODE_BITS = 6L;
    /**
     * 自增序列所占位数 (10bit, 支持最大每秒生成 2^10 = 1024)
     */
    private static final long SEQUENCE_ID_BITS = 10L;
    /**
     * 系统模块码偏移位数
     */
    private static final long MODULE_SHIFT_BITS = SEQUENCE_ID_BITS;
    /**
     * 机器id偏移位数
     */
    private static final long WORKER_SHIFT_BITS = SEQUENCE_ID_BITS + MODULE_CODE_BITS;
    /**
     * 自增序列偏移位数
     */
    private static final long OFFSET_SHIFT_BITS = SEQUENCE_ID_BITS + MODULE_CODE_BITS + WORKER_ID_BITS;
    /**
     * 机器标识最大值 (2^5 / 2 - 1 = 15)
     */
    private static final long WORKER_ID_MAX = ((1 << WORKER_ID_BITS) - 1) >> 1;
    /**
     * 备份机器ID开始位置 (2^5 / 2 = 16)
     */
    private static final long BACK_WORKER_ID_BEGIN = (1 << WORKER_ID_BITS) >> 1;
 
    /**
     * 系统模块码最大值 (2^6 - 1 = 63)
     */
    private static final long MODULE_CODE_MAX = (1 << MODULE_CODE_BITS) - 1;
    /**
     * 自增序列最大值 (2^15 - 1 = 1023)
     */
    private static final long SEQUENCE_MAX = (1 << SEQUENCE_ID_BITS) - 1;
    /**
     * 发生时间回拨时容忍的最大回拨时间 (秒)
     */
    private static final long BACK_TIME_MAX = 1L;
 
    /**
     * 上次生成ID的时间戳 (秒)
     */
    private static long lastTimestamp = 0L;
    /**
     * 当前秒内序列 (2^16)
     */
    private static long sequence = 0L;
    /**
     * 备份机器上次生成ID的时间戳 (秒)
     */
    private static long lastTimestampBak = 0L;
    /**
     * 备份机器当前秒内序列 (2^16)
     */
    private static long sequenceBak = 0L;
 
    /**
     * 私有构造函数禁止外部访问
     */
    private SequenceUtil() {
    }
 
    /**
     * 获取自增序列
     *
     * @return long
     */
    public static long generateId(long workerId, long moduleCode) {
        WORKER_ID = workerId;
        MODULE_CODE = moduleCode;
        if (WORKER_ID > WORKER_ID_MAX || WORKER_ID < 0) {
            throw new IllegalArgumentException(String.format("机器数量范围: 0 ~ %d 目前: %d", WORKER_ID_MAX, workerId));
        }
        if (MODULE_CODE > MODULE_CODE_MAX || MODULE_CODE < 0) {
            throw new IllegalArgumentException(String.format("模块数量范围: 0 ~ %d 目前: %d", MODULE_CODE_MAX, moduleCode));
        }
        return nextId(SystemClock.now() / 1000);
    }
 
    /**
     * 主机器自增序列
     *
     * @param timestamp 当前Unix时间戳
     * @return long
     */
    private static synchronized long nextId(long timestamp) {
        // 时钟回拨检查
        if (timestamp < lastTimestamp) {
            // 发生时钟回拨
            log.warn("时钟回拨, 启用备份机器ID: now: [{}] last: [{}]", timestamp, lastTimestamp);
            return nextIdBackup(timestamp);
        }
 
        // 开始下一秒
        if (timestamp != lastTimestamp) {
            lastTimestamp = timestamp;
            sequence = 0L;
        }
        if (0L == (++sequence & SEQUENCE_MAX)) {
            sequence--;
            return nextIdBackup(timestamp);
        }
 
        return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS)
                | (WORKER_ID << WORKER_SHIFT_BITS)
                | (MODULE_CODE << MODULE_SHIFT_BITS)
                | sequence;
    }
 
    /**
     * 备份机器自增序列
     *
     * @param timestamp timestamp 当前Unix时间戳
     * @return long
     */
    private static long nextIdBackup(long timestamp) {
        int toSecond = 1000;
        if (timestamp < lastTimestampBak) {
            if (lastTimestampBak - SystemClock.now() / toSecond <= BACK_TIME_MAX) {
                timestamp = lastTimestampBak;
            } else {
                throw new RuntimeException(String.format("时钟回拨: now: [%d] last: [%d]", timestamp, lastTimestampBak));
            }
        }
 
        if (timestamp != lastTimestampBak) {
            lastTimestampBak = timestamp;
            sequenceBak = 0L;
        }
 
        if (0L == (++sequenceBak & SEQUENCE_MAX)) {
            return nextIdBackup(timestamp + 1);
        }
 
        return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS)
                | (WORKER_ID << WORKER_SHIFT_BITS)
                | (MODULE_CODE << MODULE_SHIFT_BITS)
                | sequence;
    }
 
}