You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

70 lines
2.2 KiB
Go

1 year ago
package tools
import (
"github.com/metacubex/mihomo/log"
"sync"
"time"
)
const (
epoch = int64(1577808000000) // 设置起始时间(时间戳/毫秒)2020-01-01 00:00:00有效期69年
timestampBits = uint(41) // 时间戳占用位数
datacenteridBits = uint(5) // 数据中心id所占位数
workeridBits = uint(5) // 机器id所占位数
sequenceBits = uint(12) // 序列所占的位数
timestampMax = int64(-1 ^ (-1 << timestampBits)) // 时间戳最大值
sequenceMask = int64(-1 ^ (-1 << sequenceBits)) // 支持的最大序列id数量
workeridShift = sequenceBits // 机器id左移位数
datacenteridShift = sequenceBits + workeridBits // 数据中心id左移位数
timestampShift = sequenceBits + workeridBits + datacenteridBits // 时间戳左移位数
)
type snowflake struct {
sync.Mutex
timestamp int64
workerid int64
datacenterid int64
sequence int64
}
var s = &snowflake{
timestamp: 0,
datacenterid: 1,
workerid: 2,
sequence: 0,
}
func SnowflakeId() int64 {
return s.nextVal()
}
// nextVal 返回下一个Snowflake ID
func (s *snowflake) nextVal() int64 {
s.Lock()
now := time.Now().UnixNano() / 1000000 // 转毫秒
if s.timestamp == now {
// 当同一时间戳精度毫秒下多次生成id会增加序列号
s.sequence = (s.sequence + 1) & sequenceMask
if s.sequence == 0 {
// 如果当前序列超出12bit长度则需要等待下一毫秒
// 下一毫秒将使用sequence:0
for now <= s.timestamp {
now = time.Now().UnixNano() / 1000000
}
}
} else {
// 不同时间戳精度毫秒下直接使用序列号0
s.sequence = 0
}
t := now - epoch
if t > timestampMax {
s.Unlock()
log.Fatalln("epoch must be between 0 and %d", timestampMax-1)
return 0
}
s.timestamp = now
r := (t)<<timestampShift | (s.datacenterid << datacenteridShift) | (s.workerid << workeridShift) | (s.sequence)
s.Unlock()
return r
}