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;
|
}
|
|
}
|