package idgen import ( "crypto/rand" "fmt" "sync" "time" ) var ( generator *FlakeIDGenerator once sync.Once ) type FlakeIDGenerator struct { mu sync.Mutex lastTs int64 seq int64 nodeID int64 } func NewFlakeIDGenerator(nodeID int64) *FlakeIDGenerator { return &FlakeIDGenerator{ nodeID: nodeID, } } func (g *FlakeIDGenerator) GetID() (string, error) { g.mu.Lock() defer g.mu.Unlock() now := time.Now().UnixNano() / 1e6 if now < g.lastTs { return "", fmt.Errorf("clock moved backwards") } if now == g.lastTs { g.seq++ if g.seq >= 1024 { for now <= g.lastTs { now = time.Now().UnixNano() / 1e6 } g.seq = 0 } } else { g.seq = 0 } g.lastTs = now id := (now << 22) | (g.nodeID << 10) | g.seq return fmt.Sprintf("%d", id), nil } func GetID() (string, error) { once.Do(func() { nodeID := int64(1) b := make([]byte, 2) _, err := rand.Read(b) if err == nil { nodeID = int64(b[0])<<8 | int64(b[1]) nodeID &= 0x3FF } generator = NewFlakeIDGenerator(nodeID) }) return generator.GetID() } func MustGetID() string { id, err := GetID() if err != nil { return fmt.Sprintf("%d", time.Now().UnixNano()) } return id }