Files
redis-go/lib/idgenerator/snowflake.go
2021-05-13 08:56:07 +08:00

71 lines
1.6 KiB
Go

package idgenerator
import (
"hash/fnv"
"log"
"sync"
"time"
)
const (
// epoch0 is set to the twitter snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds
// You may customize this to set a different epoch for your application.
epoch0 int64 = 1288834974657
maxSequence int64 = -1 ^ (-1 << uint64(nodeLeft))
timeLeft uint8 = 22
nodeLeft uint8 = 10
nodeMask int64 = -1 ^ (-1 << uint64(timeLeft-nodeLeft))
)
// IDGenerator generates unique uint64 ID using snowflake algorithm
type IDGenerator struct {
mu *sync.Mutex
lastStamp int64
nodeID int64
sequence int64
epoch time.Time
}
// MakeGenerator creates a new IDGenerator
func MakeGenerator(node string) *IDGenerator {
fnv64 := fnv.New64()
_, _ = fnv64.Write([]byte(node))
nodeID := int64(fnv64.Sum64()) & nodeMask
var curTime = time.Now()
epoch := curTime.Add(time.Unix(epoch0/1000, (epoch0%1000)*1000000).Sub(curTime))
return &IDGenerator{
mu: &sync.Mutex{},
lastStamp: -1,
nodeID: nodeID,
sequence: 1,
epoch: epoch,
}
}
// NextID returns next unique ID
func (w *IDGenerator) NextID() int64 {
w.mu.Lock()
defer w.mu.Unlock()
timestamp := time.Since(w.epoch).Nanoseconds() / 1000000
if timestamp < w.lastStamp {
log.Fatal("can not generate id")
}
if w.lastStamp == timestamp {
w.sequence = (w.sequence + 1) & maxSequence
if w.sequence == 0 {
for timestamp <= w.lastStamp {
timestamp = time.Since(w.epoch).Nanoseconds() / 1000000
}
}
} else {
w.sequence = 0
}
w.lastStamp = timestamp
id := (timestamp << timeLeft) | (w.nodeID << nodeLeft) | w.sequence
//fmt.Printf("%d %d %d\n", timestamp, w.sequence, id)
return id
}