1. 源码和使用场景
源码地址位于github snowFlake
SnowFlake可以保证:
- 所有生成的id按时间趋势递增
- 整个分布式系统内不会产生重复id(使用datacenterId和workerId来区分)
2. 基于源码进行修改
修改后的程序如下
/** Copyright 2010-2012 Twitter, Inc.*/
/** 根据如下的源码进行修改的
* https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala */
/**
* An object that generates IDs.
* This is broken into a separate class in case
* we ever want to support multiple worker threads
* per process
*/
class IdWorker(val workerId: Long, val datacenterId: Long, var sequence: Long = 0L) {
val twepoch = 1288834974657L
private[this] val workerIdBits = 5L
private[this] val datacenterIdBits = 5L
private[this] val maxWorkerId = -1L ^ (-1L << workerIdBits)
private[this] val maxDatacenterId = -1L ^ (-1L << datacenterIdBits)
private[this] val sequenceBits = 12L
private[this] val workerIdShift = sequenceBits
private[this] val datacenterIdShift = sequenceBits + workerIdBits
private[this] val timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits
private[this] val sequenceMask = -1L ^ (-1L << sequenceBits)
private[this] var lastTimestamp = -1L
// sanity check for workerId
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0".format(maxWorkerId))
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0".format(maxDatacenterId))
}
println(s"worker starting. timestamp left shift ${timestampLeftShift}, " +
s"datacenter id bits ${datacenterIdBits}, worker id bits ${workerIdBits}, " +
s"sequence bits ${sequenceBits}, workerid ${workerId}")
def get_worker_id(): Long = workerId
def get_datacenter_id(): Long = datacenterId
def get_timestamp() = System.currentTimeMillis
def nextId(): Long = synchronized {
var timestamp = timeGen()
if (timestamp < lastTimestamp) {
printf(s"clock is moving backwards. Rejecting requests until ${lastTimestamp}.")
throw new RuntimeException("Clock moved backwards. Refusing to generate id for %d milliseconds".format(
lastTimestamp - timestamp))
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp)
}
} else {
sequence = 0
}
lastTimestamp = timestamp
((timestamp - twepoch) << timestampLeftShift) |
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence
}
protected def tilNextMillis(lastTimestamp: Long): Long = {
var timestamp = timeGen()
while (timestamp <= lastTimestamp) {
timestamp = timeGen()
}
timestamp
}
protected def timeGen(): Long = System.currentTimeMillis()
}
3. 生成分布式的ID
import java.net.InetAddress
object IdWorkerTest {
def main(args: Array[String]): Unit = {
// 暂时采用获取IP,然后取模的方法。可能会存在两台服务器产生相同的workId,产生相同的ID
val ip:String = InetAddress.getLocalHost().getHostAddress()
val workerId:Long = ip.replaceAll("\\.", "").toLong
val idWorker = new IdWorker(workerId % 32, 1)
for (index <- 1 to 10) {
println(idWorker.nextId())
}
}
}
运行程序,结果如下
worker starting. timestamp left shift 22, datacenter id bits 5, worker id bits 5, sequence bits 12, workerid 7
1535189744345051136
1535189744345051137
1535189744345051138
1535189744345051139
1535189744345051140
1535189744345051141
1535189744345051142
1535189744345051143
1535189744345051144
1535189744345051145