update Snowflake

This commit is contained in:
tursom 2020-07-05 03:03:27 +08:00
parent 53369b699d
commit aba4829a7b
2 changed files with 95 additions and 35 deletions

View File

@ -1,9 +1,14 @@
package cn.tursom.core
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong
/**
* 被改造过的雪花ID
* |---------|------------|-----------|------------|
* | 空位(1) | 时间戳(43) | 自增位(7) | 机器ID(13) |
* |---------|------------|-----------|------------|
* 时间戳仅在初始化时使用, 与序列号接壤, 这样做可以避免某一时间内大量请求导致的ID爆炸
* 当前方案在ID溢出时, 溢出的数据会让时间戳 +1.
* 这样做, 只要节点不重启或者在重启前平均QPS没有超标, 重启后分配的ID仍能唯一
@ -14,37 +19,77 @@ import java.util.concurrent.atomic.AtomicLong
*
* 当前最多支持 8192 个节点同时上线, 未来如果节点数超过了 8192 , 也可以以ID生成的最晚时间为代价提升节点最高数量
*/
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter", "unused")
class Snowflake(
@Suppress("MemberVisibilityCanBePrivate") val nodeId: Int
@Suppress("MemberVisibilityCanBePrivate") val nodeId: Int,
val executorService: ScheduledExecutorService? = null,
val updateRateMs: Long = defaultUpdateRateMs,
val workMode: WorkMode = WorkMode.INCREMENT
) {
constructor(workerId: String) : this(parseId(workerId))
constructor(
workerId: String,
executorService: ScheduledExecutorService? = null,
updateRateMs: Long = defaultUpdateRateMs,
workMode: WorkMode = WorkMode.INCREMENT
) : this(parseId(workerId), executorService, updateRateMs, workMode)
@Suppress("MemberVisibilityCanBePrivate")
val timestamp = System.currentTimeMillis().and(0x00_00_07_ff__ff_ff_ff_ff).shl(7)
private val seed = AtomicLong((nodeId and 0x1fff).toLong().shl(50) or timestamp)
val id: Long get() = seed.incrementAndGet()
override fun toString() = "Snowflake(workerId=${nodeId
}, timestamp=0x${timestamp.toByteArray().toHexString()
}, seed=0x${seed.get().toByteArray().toHexString()})"
companion object {
fun parseId(workerId: String): Int {
var id = 0
var step = 1
for (index in workerId.length - 1 downTo 0) {
if (Character.isDigit(workerId[index])) {
id += (workerId[index] - '0') * step
step *= 10
} else {
break
}
}
if (step != 1) {
return id
} else {
throw NumberFormatException(workerId)
}
}
private var _timestamp = System.currentTimeMillis().and(0x00_00_07_ff__ff_ff_ff_ff).shl(7 + 13)
set(value) {
field = value
seed = AtomicLong((nodeId and 0x1fff).toLong() or field)
}
val timestamp: Long
get() = _timestamp
private var seed = AtomicLong((nodeId and 0x1fff).toLong() or timestamp)
val id: Long get() = seed.getAndAdd(incrementBase)
init {
executorService?.scheduleWithFixedDelay({
when (workMode) {
WorkMode.REALTIME -> {
val timestamp = System.currentTimeMillis()
.and(0x00_00_07_ff__ff_ff_ff_ff)
.shl(7 + 13)
if (seed.get() < timestamp - 0x10_00_00) {
_timestamp = timestamp
}
}
WorkMode.INCREMENT -> _timestamp += 0x10_00_00
WorkMode.NO_INCREMENT -> {
}
}
}, updateRateMs, updateRateMs, TimeUnit.MICROSECONDS)
}
override fun toString() = "Snowflake(workerId=${nodeId
}, timestamp=0x${timestamp.toByteArray().toHexString(false)
}, seed=0x${seed.get().toByteArray().toHexString(false)})"
enum class WorkMode {
REALTIME, INCREMENT, NO_INCREMENT
}
companion object {
const val defaultUpdateRateMs = 16L
const val incrementBase = 0x2000L
fun parseId(workerId: String): Int {
var id = 0
var step = 1
for (index in workerId.length - 1 downTo 0) {
if (Character.isDigit(workerId[index])) {
id += (workerId[index] - '0') * step
step *= 10
} else {
break
}
}
if (step != 1) {
return id
} else {
throw NumberFormatException(workerId)
}
}
}
}

View File

@ -17,6 +17,7 @@ import java.util.concurrent.Executor
import java.util.jar.JarFile
import java.util.zip.*
import kotlin.collections.ArrayList
import kotlin.experimental.and
inline fun <reified T> Array<out T?>.excludeNull(): List<T> {
@ -169,13 +170,27 @@ fun ByteArray.sha512(): ByteArray = sha512.digest(this)
fun String.sha512(): String = toByteArray().sha512().toHexString()
private val HEX_ARRAY = "0123456789abcdef".toCharArray()
fun ByteArray.toHexString(): String {
fun ByteArray.toHexString(upper: Boolean = true): String = if (upper) toUpperHexString() else toLowerHexString()
private val UPPER_HEX_ARRAY = "0123456789ABCDEF".toCharArray()
fun ByteArray.toUpperHexString(): String {
val hexChars = CharArray(size * 2)
for (i in indices) {
val b = this[i].toInt()
hexChars[i shl 1] = HEX_ARRAY[b ushr 4 and 0x0F]
hexChars[(i shl 1) + 1] = HEX_ARRAY[b and 0x0F]
val b = this[i]
hexChars[i shl 1] = UPPER_HEX_ARRAY[b.toInt() ushr 4 and 0x0F]
hexChars[(i shl 1) + 1] = UPPER_HEX_ARRAY[(b and 0x0F).toInt()]
}
return String(hexChars)
}
private val LOWER_HEX_ARRAY = "0123456789abcdef".toCharArray()
fun ByteArray.toLowerHexString(): String {
val hexChars = CharArray(size * 2)
for (i in indices) {
val b = this[i]
hexChars[i shl 1] = LOWER_HEX_ARRAY[b.toInt() ushr 4 and 0x0F]
hexChars[(i shl 1) + 1] = LOWER_HEX_ARRAY[(b and 0x0F).toInt()]
}
return String(hexChars)
}
@ -184,7 +199,7 @@ fun String.fromHexString(): ByteArray {
val source = toLowerCase()
val data = ByteArray(length / 2)
for (i in 0 until length / 2) {
data[i] = ((HEX_ARRAY.indexOf(source[i * 2]) shl 4) + HEX_ARRAY.indexOf(source[i * 2 + 1])).toByte()
data[i] = ((LOWER_HEX_ARRAY.indexOf(source[i * 2]) shl 4) + LOWER_HEX_ARRAY.indexOf(source[i * 2 + 1])).toByte()
}
return data
}