package snowflake
import (
"fmt"
"sync"
"time"
)
// 算法模式如下:
// | 1 | 41 | 10 | 12 |
// | sign | timestamp | workerId | sequence |
const (
baseTime int64 = 1514736000000 // 2018-01-01 00:00:00为时间基
sequenceMax int64 = 0xfff // 最大的序号
workerMaxID int64 = 0x3ff // 最大的业务ID
)
type SnowFlake struct {
workerId int64 // 当前的业务ID
sequence int64 // 当前毫秒时刻ID的序号
lastTime int64 // 最后记录的时间戳
mutex sync.Mutex
}
func NewSnowFlake(wID int64) (*SnowFlake, error) {
if wID > workerMaxID || wID < 0 {
return nil, fmt.Errorf("WorkerId %d invalid.", wID)
}
return &SnowFlake{workerId: wID}, nil
}
func (s *SnowFlake) NextID() (int64, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
nowTime := time.Now().UnixNano() / 1e6
if nowTime < s.lastTime {
return 0, fmt.Errorf("Clock is moving backwards. Rejecting requests until %d.", s.lastTime)
}
if nowTime == s.lastTime {
s.sequence = (s.sequence + 1) & sequenceMax
if s.sequence == 0 {
for nowTime <= s.lastTime {
nowTime = time.Now().UnixNano() / 1e6
}
}
} else {
s.sequence = 0
}
s.lastTime = nowTime
return (nowTime-baseTime)<<22 | s.workerId<<12 | s.sequence, nil
}
关键是解决workerId的重复问题,推荐使用redis在应用启动时锁定一个1024个workerId中的一个,设置好过期时间,并在过期之前激活,循环往复即可。