我有以下代码来生成唯一的eventId。这不是纯GUID生成器,而是一种在不同机器/进程之间生成唯一ID的方法。
import java.util.concurrent.atomic.AtomicInteger
object EventIdGenerator extends Serializable {
val totalBits = 64
private val epochBits = 42
private val partitionIdBits = 10
private val sequenceBits = 12
private val sequenceInt = new AtomicInteger(0)
private val maxPartitionId = (Math.pow(2, partitionIdBits) - 1).toInt
private val maxSequenceId = (Math.pow(2, sequenceBits) - 1).toInt
def nextEventId(partitionId: Int): Long = {
assert(partitionId <= maxPartitionId)
val nextSequence = sequenceInt.incrementAndGet() % maxSequenceId
val timestamp = if (nextSequence == 0) {
Thread.sleep(1)
System.currentTimeMillis()
} else System.currentTimeMillis()
timestamp << (totalBits - epochBits) |
partitionId << (totalBits - epochBits - partitionIdBits) |
nextSequence
}
}
应该从在不同机器上的多个JVM上运行的分布式程序中调用它。在这里,分区ID在不同的JVM和机器之间将是唯一的。
当我在单个线程中运行该程序时,它可以正常工作。但是,当我在多个线程中运行每个线程使用唯一的分区ID调用nextEventId时,有时会得到重复的事件ID。我无法弄清楚下面的代码是什么问题。
答案 0 :(得分:1)
nextSequence
仅具有1024个不同的值。因此,如果您的请求速率超过每毫秒1024个(对于1个分区),则有约100%的冲突机会。实际上,由于时钟精度,我认为冲突将以更小的速率出现。
我发现,如果您检测到sleep(1)
溢出,则尝试与nextSequence
回避。但这在多线程环境中不起作用。特定分区甚至可能永远不会看到nextSequence
等于0。