91 lines
1.7 KiB
Go
91 lines
1.7 KiB
Go
package uid
|
|
|
|
import (
|
|
"base-go-v2/internal/config"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
datacenterBits = uint64(5)
|
|
workerBits = uint64(5)
|
|
sequenceBits = uint64(12)
|
|
|
|
maxSequence = -1 ^ (-1 << sequenceBits)
|
|
workerShift = sequenceBits
|
|
datacenterShift = sequenceBits + workerBits
|
|
timestampShift = sequenceBits + workerBits + datacenterBits
|
|
|
|
epoch = int64(1672531200000) // 2023-01-01
|
|
)
|
|
|
|
type Snowflake struct {
|
|
lastStamp uint64 // 上一次时间戳 (毫秒)
|
|
sequence uint64 // 序列号
|
|
dcID uint64
|
|
workerID uint64
|
|
}
|
|
|
|
var (
|
|
sfInstance *Snowflake
|
|
once sync.Once
|
|
)
|
|
|
|
// lazy 初始化实例
|
|
func initInstance() {
|
|
var dcID, wID uint64
|
|
|
|
dcID = config.App.Snowflake.DatacenterID
|
|
wID = config.App.Snowflake.WorkerID
|
|
|
|
sfInstance = &Snowflake{
|
|
dcID: dcID,
|
|
workerID: wID,
|
|
}
|
|
}
|
|
|
|
// NextID 生成全局唯一 ID,无锁版本
|
|
func NextID() uint64 {
|
|
once.Do(initInstance)
|
|
|
|
for {
|
|
now := uint64(time.Now().UnixNano() / 1e6)
|
|
|
|
last := atomic.LoadUint64(&sfInstance.lastStamp)
|
|
seq := atomic.LoadUint64(&sfInstance.sequence)
|
|
|
|
if now < last {
|
|
// 系统时钟回拨,等待
|
|
now = waitNextMillis(last)
|
|
}
|
|
|
|
if now == last {
|
|
seq = (seq + 1) & uint64(maxSequence)
|
|
if seq == 0 {
|
|
now = waitNextMillis(last)
|
|
}
|
|
} else {
|
|
seq = 0
|
|
}
|
|
|
|
if atomic.CompareAndSwapUint64(&sfInstance.lastStamp, last, now) {
|
|
atomic.StoreUint64(&sfInstance.sequence, seq)
|
|
id := ((now - uint64(epoch)) << timestampShift) |
|
|
(sfInstance.dcID << datacenterShift) |
|
|
(sfInstance.workerID << workerShift) |
|
|
seq
|
|
return id
|
|
}
|
|
}
|
|
}
|
|
|
|
// 等待下一个毫秒
|
|
func waitNextMillis(last uint64) uint64 {
|
|
now := uint64(time.Now().UnixNano() / 1e6)
|
|
for now <= last {
|
|
now = uint64(time.Now().UnixNano() / 1e6)
|
|
}
|
|
return now
|
|
}
|