content: package com.admin4j.common;
  
  
  import lombok.SneakyThrows;
  import lombok.extern.slf4j.Slf4j;
  import org.apache.commons.lang3.StringUtils;
  
  import java.lang.management.ManagementFactory;
  import java.net.InetAddress;
  import java.net.NetworkInterface;
  import java.util.Optional;
  import java.util.concurrent.ThreadLocalRandom;
  
  /**
   * 雪花算法
   *
   * @author: andanyang
   * @date: 2021-08-05 11:20
   */
  @Slf4j
  public class SnowflakeIdWorker {
  
      /**
       * 时间起始标记点，作为基准，一般取系统的最近时间（一旦确定不能变动）
       */
      private long twepoch = 1639282332000L;
      /**
       * 机器标识位数
       */
      private final long workerIdBits = 5L;
      private final long datacenterIdBits = 5L;
      private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
      private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
      /**
       * 毫秒内自增位
       */
      private final long sequenceBits = 12L;
      private final long workerIdShift = sequenceBits;
      private final long datacenterIdShift = sequenceBits + workerIdBits;
      /**
       * 时间戳左移动位
       */
      private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
      private final long sequenceMask = -1L ^ (-1L << sequenceBits);
  
      private final long workerId;
  
      /**
       * 数据标识 ID 部分
       */
      private final long datacenterId;
      /**
       * 并发控制
       */
      private long sequence = 0L;
      /**
       * 上次生产 ID 时间戳
       */
      private long lastTimestamp = -1L;
  
      private InetAddress inetAddress;
  
      public SnowflakeIdWorker() {
          InetAddress inetAddress = getInetAddress();
          this.inetAddress = inetAddress;
          this.datacenterId = getDatacenterId(maxDatacenterId);
          this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
      }
  
      public SnowflakeIdWorker(long twepoch) {
          this();
          this.twepoch = twepoch;
      }
  
      public SnowflakeIdWorker(InetAddress inetAddress) {
          this.inetAddress = inetAddress;
          this.datacenterId = getDatacenterId(maxDatacenterId);
          this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
      }
  
      @SneakyThrows
      private InetAddress getLocalHost() {
  
          return InetAddress.getLocalHost();
      }
  
      /**
       * @return InetAddress
       * @since 3.4.3
       */
      protected InetAddress getInetAddress() {
          return Optional.ofNullable(this.inetAddress).orElseGet(this::getLocalHost);
      }
  
      /**
       * 有参构造器
       *
       * @param workerId     工作机器 ID
       * @param datacenterId 序列号
       */
      public SnowflakeIdWorker(long workerId, long datacenterId) {
          if (workerId > maxWorkerId) {
              throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
          }
          if (datacenterId >= maxDatacenterId) {
              throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  
          }
          //Assert.isTrue(workerId <= maxWorkerId || workerId < 0,
          //        String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
          //Assert.isTrue(datacenterId <= maxDatacenterId || datacenterId < 0,
          //        String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
          this.workerId = workerId;
          this.datacenterId = datacenterId;
      }
  
      /**
       * 获取 maxWorkerId
       */
      protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {
          StringBuilder mpid = new StringBuilder();
          mpid.append(datacenterId);
          String name = ManagementFactory.getRuntimeMXBean().getName();
          if (StringUtils.isNotBlank(name)) {
              /*
               * GET jvmPid
               */
              mpid.append(name.split("@")[0]);
          }
          /*
           * MAC + PID 的 hashcode 获取16个低位
           */
          return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
      }
  
      /**
       * 数据标识id部分
       */
      protected long getDatacenterId(long maxDatacenterId) {
          long id = 0L;
          try {
              NetworkInterface network = NetworkInterface.getByInetAddress(this.getInetAddress());
              if (network == null) {
                  id = 1L;
              } else {
                  byte[] mac = network.getHardwareAddress();
                  if (null != mac) {
                      id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
                      id = id % (maxDatacenterId + 1);
                  }
              }
          } catch (Exception e) {
              log.warn(" getDatacenterId: " + e.getMessage());
          }
          return id;
      }
  
      /**
       * 获取下一个 ID
       *
       * @return 下一个 ID
       */
      public synchronized long nextId() {
          long timestamp = timeGen();
          //闰秒
          if (timestamp < lastTimestamp) {
              //检测时钟回拨
              long offset = lastTimestamp - timestamp;
              if (offset <= 5) {
                  try {
                      wait(offset << 1);
                      timestamp = timeGen();
                      if (timestamp < lastTimestamp) {
                          throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                      }
                  } catch (Exception e) {
                      throw new RuntimeException(e);
                  }
              } else {
                  throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
              }
          }
  
          if (lastTimestamp == timestamp) {
              // 相同毫秒内，序列号自增
              sequence = (sequence + 1) & sequenceMask;
              if (sequence == 0) {
                  // 同一毫秒的序列数已经达到最大
                  timestamp = tilNextMillis(lastTimestamp);
              }
          } else {
              // 不同毫秒内，序列号置为 1 - 3 随机数
              sequence = ThreadLocalRandom.current().nextLong(1, 3);
          }
  
          lastTimestamp = timestamp;
  
          // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
          return ((timestamp - twepoch) << timestampLeftShift)
                  | (datacenterId << datacenterIdShift)
                  | (workerId << workerIdShift)
                  | sequence;
      }
  
      protected long tilNextMillis(long lastTimestamp) {
          long timestamp = timeGen();
          while (timestamp <= lastTimestamp) {
              timestamp = timeGen();
          }
          return timestamp;
      }
  
      protected long timeGen() {
          return System.currentTimeMillis();
      }
  
  }
  