1. package com.seven.base.util;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.springframework.beans.factory.annotation.Value;
    4. import org.springframework.stereotype.Service;
    5. import javax.annotation.PostConstruct;
    6. /**
    7. * @author deng
    8. * @Version V1.0.0
    9. * @ClassName SnowId
    10. * @date 2022/3/1 18:06
    11. * @Description 雪花id生成
    12. */
    13. @Slf4j
    14. @Service
    15. public class SnowUtil {
    16. //region ==============================Fields===========================================
    17. /** 请在nacos上为每一个微服务配置一个不同的workerId */
    18. @Value("${snow.workerId}")
    19. private int workerId;
    20. /** 开始时间截 (2022-01-01 08:08:08) */
    21. private static final long START_EPOCH = 1640995688000L;
    22. /** 雪花算法id的总位数,注意第一位是不用的 */
    23. private static final long DATA_BITS = 64L;
    24. /** 机器id所占的位数,精确到毫秒,可以用69年 */
    25. private static final long TIMESTAMP_BITS = 41L;
    26. /** 机器id所占的位数 */
    27. private static final long WORKER_ID_BITS = 10L;
    28. /** 序列在id中占的位数 */
    29. private static final long SEQUENCE_BITS = 12L;
    30. /** 支持的最大机器id范围(0~1023) */
    31. private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    32. /** 生成序列的掩码,这里为2的12次方-1范围(0~4095) */
    33. private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
    34. /** 时间截向左移22位 */
    35. private static final long TIMESTAMP_LEFT_SHIFT = DATA_BITS - 1 - TIMESTAMP_BITS;
    36. /** 机器ID向左移12位 */
    37. private static final long WORKER_ID_LEFT_SHIFT = SEQUENCE_BITS;
    38. /** 毫秒内序列范围(0~4095) */
    39. private static long sequence = 0L;
    40. /** 上次生成ID的时间截 */
    41. private static long lastTimestamp = -1L;
    42. //endregion
    43. //region ==============================Constructors=====================================
    44. /** 定义静态的util对象 */
    45. private static SnowUtil snowUtil;
    46. /**
    47. * 构造函数
    48. */
    49. private SnowUtil(){ }
    50. //endregion
    51. //region ==============================Methods==========================================
    52. /**
    53. * workerId初始化
    54. */
    55. @PostConstruct
    56. private void init(){
    57. snowUtil = this;
    58. }
    59. /**
    60. * 获取id
    61. */
    62. public static long nextId() throws Exception {
    63. //最好的做法是在nacos上为每一个不同的微服务都配置一个唯一的snow workerId,这样不同的微服务之间就不会生成相同的id了
    64. return nextId(snowUtil.workerId);
    65. }
    66. /**
    67. * 获得下一个ID (该方法是线程安全的)
    68. * @param workerId 工作机器ID(0~31)
    69. * @return SnowflakeId
    70. */
    71. protected static synchronized long nextId(long workerId) throws Exception {
    72. //workerId判断
    73. if (workerId > MAX_WORKER_ID || workerId < 0) {
    74. String err = String.format("SnowId Class error, workerId: %d,minWorkerId: %d,maxWorkerId: %d,please check", workerId, 0, MAX_WORKER_ID);
    75. log.error(err);
    76. throw new Exception(err);
    77. }
    78. //获取当前时间戳
    79. long timestamp = System.currentTimeMillis();
    80. //如果当前时间戳小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
    81. if (timestamp < lastTimestamp) {
    82. String err = String.format("SnowId Class error,Clock moved backwards, lastTimestamp: %d,nowTimestamp: %d,diffTimestamp: %d,please check", lastTimestamp, timestamp, lastTimestamp - timestamp);
    83. log.error(err);
    84. throw new Exception(err);
    85. }
    86. //如果是同一时间生成的,则进行毫秒内序列
    87. if (lastTimestamp == timestamp) {
    88. sequence = (sequence + 1) & SEQUENCE_MASK;
    89. //毫秒内序列溢出
    90. if (sequence == 0) {
    91. //阻塞到下一个毫秒,获得新的时间戳
    92. timestamp = tilNextMillis(lastTimestamp);
    93. }
    94. }else {
    95. //时间戳改变,毫秒内序列重置
    96. sequence = 0L;
    97. }
    98. //更新上次生成ID的时间截
    99. lastTimestamp = timestamp;
    100. // 1位高位,41位时间戳,10位workerId,12位同一时间毫秒内能生成的序列号码
    101. // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000
    102. // 高位0,0与任何数字按位或运算都是本身,所以第一位高位写不写效果都一样
    103. // timestamp、workerId左移,sequence不需要移动,低位补0
    104. // 按位或运算进行二进制拼接
    105. return ((timestamp - START_EPOCH) << TIMESTAMP_LEFT_SHIFT)
    106. | (workerId << WORKER_ID_LEFT_SHIFT)
    107. | sequence;
    108. }
    109. /**
    110. * 阻塞到下一个毫秒,直到获得新的时间戳
    111. * @param lastTimestamp 上次生成ID的时间截
    112. * @return 当前时间戳
    113. */
    114. protected static long tilNextMillis(long lastTimestamp) {
    115. long timestamp = System.currentTimeMillis();
    116. while (timestamp <= lastTimestamp) {
    117. timestamp = System.currentTimeMillis();
    118. }
    119. return timestamp;
    120. }
    121. //endregion
    122. }