Files
chain33-dtalk/pkg/util/snowflake.go
2022-03-17 15:59:24 +08:00

84 lines
3.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package util
import (
"errors"
"sync"
"time"
)
// +-----------------------------------------------------------------------+
// | UNUSED(1BIT) | TIMESTAMP(41BIT) | NODE-ID(10BIT) | SEQUENCE-ID(12BIT) |
// +-----------------------------------------------------------------------+
// snowflake算法用于生成分布式系统下全局唯一id64位
// 1位符号位
// 41位时间戳毫秒级为当前时间戳-初始时间戳大约能使用从初始时间戳开始至初始时间戳69年后
// 10位节点id
// 12位序列id
const (
nodeBits uint8 = 10 // 节点id所占的位数10位表示最多可以有2^10=1024个节点
sequenceBits uint8 = 12 // 序列id所占的位数12位表示每台机器1毫秒内最多可以生成2^12=4096个唯一id
nodeMax int64 = -1 ^ (-1 << nodeBits) // 节点id的最大值位运算用于防止溢出默认为1023
sequenceMax int64 = -1 ^ (-1 << sequenceBits) // 序列id的最大值位运算用于防止溢出默认为4095
timestampShift uint8 = nodeBits + sequenceBits // 时间戳左移位数默认为22
nodeShift uint8 = sequenceBits // 节点id左移位数默认为12
epoch int64 = 1590940800000 // 初始时间戳默认为2020-06-01 00:00:00的毫秒级时间戳正式使用后不可修改
)
// Snowflake 定义一个Snowflake节点所需要的基本参数
type Snowflake struct {
sync.Mutex // 互斥锁,用于确保并发安全
timestamp int64 // 当前时间戳(毫秒级)
nodeId int64 // 当前节点的id
sequenceId int64 // 当前毫秒下已经生成的序列id默认在1毫秒内最多生成4096个
}
// NewSnowflake 实例化一个Snowflake节点
func NewSnowflake(nodeId int64, location ...*time.Location) (*Snowflake, error) {
// 判断nodeId是否非法
if nodeId < 0 || nodeId > nodeMax {
return nil, errors.New("node id excess of quantity")
}
return &Snowflake{
timestamp: TimeNowUnixMilli(location...),
nodeId: nodeId,
sequenceId: 0,
}, nil
}
// NextId 获取唯一id
func (s *Snowflake) NextId(location ...*time.Location) int64 {
s.Lock()
defer s.Unlock()
// 获取当前时间的毫秒级时间戳
now := TimeNowUnixMilli(location...)
// 发生时钟回拨时等待时间重回timestamp记录的时间
if s.timestamp > now {
for now <= s.timestamp {
SleepMilli(s.timestamp - now)
now = TimeNowUnixMilli(location...)
}
}
if s.timestamp == now {
s.sequenceId++
// 判断当前节点是否在1毫秒内生成了sequenceMax个id
if s.sequenceId > sequenceMax {
// 如果当前节点在1毫秒内生成的id超过了上限则等待1毫秒后再继续生成id
for now <= s.timestamp {
SleepMilli(1)
now = TimeNowUnixMilli(location...)
}
}
} else {
// 如果当前时间戳与节点上一次生成id的时间戳不一致则重置节点的序列id
s.sequenceId = 0
s.timestamp = now // 将当前节点上一次生成id的时间戳更新为当前时间戳
}
id := int64((now-epoch)<<timestampShift | (s.nodeId << nodeShift) | (s.sequenceId))
return id
}