package com.seven.base.util;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;/** * @author deng * @Version V1.0.0 * @ClassName SnowId * @date 2022/3/1 18:06 * @Description 雪花id生成 */@Slf4j@Servicepublic class SnowUtil { //region ==============================Fields=========================================== /** 请在nacos上为每一个微服务配置一个不同的workerId */ @Value("${snow.workerId}") private int workerId; /** 开始时间截 (2022-01-01 08:08:08) */ private static final long START_EPOCH = 1640995688000L; /** 雪花算法id的总位数,注意第一位是不用的 */ private static final long DATA_BITS = 64L; /** 机器id所占的位数,精确到毫秒,可以用69年 */ private static final long TIMESTAMP_BITS = 41L; /** 机器id所占的位数 */ private static final long WORKER_ID_BITS = 10L; /** 序列在id中占的位数 */ private static final long SEQUENCE_BITS = 12L; /** 支持的最大机器id范围(0~1023) */ private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); /** 生成序列的掩码,这里为2的12次方-1范围(0~4095) */ private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); /** 时间截向左移22位 */ private static final long TIMESTAMP_LEFT_SHIFT = DATA_BITS - 1 - TIMESTAMP_BITS; /** 机器ID向左移12位 */ private static final long WORKER_ID_LEFT_SHIFT = SEQUENCE_BITS; /** 毫秒内序列范围(0~4095) */ private static long sequence = 0L; /** 上次生成ID的时间截 */ private static long lastTimestamp = -1L; //endregion //region ==============================Constructors===================================== /** 定义静态的util对象 */ private static SnowUtil snowUtil; /** * 构造函数 */ private SnowUtil(){ } //endregion //region ==============================Methods========================================== /** * workerId初始化 */ @PostConstruct private void init(){ snowUtil = this; } /** * 获取id */ public static long nextId() throws Exception { //最好的做法是在nacos上为每一个不同的微服务都配置一个唯一的snow workerId,这样不同的微服务之间就不会生成相同的id了 return nextId(snowUtil.workerId); } /** * 获得下一个ID (该方法是线程安全的) * @param workerId 工作机器ID(0~31) * @return SnowflakeId */ protected static synchronized long nextId(long workerId) throws Exception { //workerId判断 if (workerId > MAX_WORKER_ID || workerId < 0) { String err = String.format("SnowId Class error, workerId: %d,minWorkerId: %d,maxWorkerId: %d,please check", workerId, 0, MAX_WORKER_ID); log.error(err); throw new Exception(err); } //获取当前时间戳 long timestamp = System.currentTimeMillis(); //如果当前时间戳小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp < lastTimestamp) { String err = String.format("SnowId Class error,Clock moved backwards, lastTimestamp: %d,nowTimestamp: %d,diffTimestamp: %d,please check", lastTimestamp, timestamp, lastTimestamp - timestamp); log.error(err); throw new Exception(err); } //如果是同一时间生成的,则进行毫秒内序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & SEQUENCE_MASK; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } }else { //时间戳改变,毫秒内序列重置 sequence = 0L; } //更新上次生成ID的时间截 lastTimestamp = timestamp; // 1位高位,41位时间戳,10位workerId,12位同一时间毫秒内能生成的序列号码 // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000 // 高位0,0与任何数字按位或运算都是本身,所以第一位高位写不写效果都一样 // timestamp、workerId左移,sequence不需要移动,低位补0 // 按位或运算进行二进制拼接 return ((timestamp - START_EPOCH) << TIMESTAMP_LEFT_SHIFT) | (workerId << WORKER_ID_LEFT_SHIFT) | sequence; } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected static long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } //endregion}