雪花算法单线程实现-scala

发布时间 2023-08-22 21:15:36作者: jentreywang

雪花算法单线程实现-scala

参考blog

/**
 * [时间戳][数据标识id][机器id]
 */
object SnowFlake {
  // 开始时间(ms) 2023-08-01 00:00:00
  private val startTimestamp = 1690819200000L

  // 机器id所占的位数
  private val workerIdBits = 5L

  // 数据标识id所占的位数
  private val datacenterIdBits = 5L

  // 支持的最大机器id
  private val maxWorkerId = -1L ^ (-1L << workerIdBits)

  // 支持的最大数据标识id
  private val maxDatacenterId = -1L ^ (-1L << datacenterIdBits)

  // 序列在id中占的位数
  private val sequenceBits = 12L

  // 机器id左移位数
  private val workerIdShift = sequenceBits

  // 数据标识id向左移位
  private val datacenterIdShift = sequenceBits + workerIdBits

  // 时间截向左移
  private val timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits

  // 生成序列的掩码
  private val sequenceMask = -1L ^ (-1L << sequenceBits)

  // 毫秒内序列
  private var sequence = 0L

  // 上次生成ID的时间截
  private var lastTimestamp = -1L
  
  def apply(
             workerId: Long, // 工作机器id
             datacenterId: Long // 数据中心id
           ): Long = {

    if (workerId > maxWorkerId || workerId < 0) {
      throw new IllegalArgumentException(s"workerId=[$maxWorkerId] 数据范围错误")
    }
    if (datacenterId > maxDatacenterId || datacenterId < 0) {
      throw new IllegalArgumentException(s"datacenterId=[$maxDatacenterId] 数据范围错误")
    }

    var timestamp: Long = timeGen()
    if (timestamp < lastTimestamp) {
      throw new RuntimeException("当前时间小于上一次记录的时间戳")
    }

    if (timestamp == lastTimestamp) {
      sequence = (sequence + 1) & sequenceMask
      if (sequence == 0L) {
        timestamp = tilNextMillis(lastTimestamp)
      }
    } else {
      sequence = 0L
    }

    lastTimestamp = timestamp

    //                 时间戳                      |        数据中心                      |       机器标识               | 序列号
    ((timestamp - startTimestamp) << timestampLeftShift) |(datacenterId << datacenterIdShift) |(workerId << workerIdShift) | sequence
  }

  private def timeGen() : Long = {
    System.currentTimeMillis()
  }

  private def tilNextMillis(lastTimestamp: Long): Long = {
    var timestamp: Long = timeGen()
    while (timestamp <= lastTimestamp) {
      timestamp = timeGen()
    }
    timestamp
  }

  def main(args: Array[String]): Unit = {
    for (_ <- 1 to 10) {
      val id: Long = SnowFlake(1L, 1L)
      println(id)
    }
  }

}