golang-yitisheng-server/server/common/snowflake/snowflake.go

123 lines
3.6 KiB
Go
Raw 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 snowflake
import (
"errors"
"fmt"
"sync"
"time"
)
// 定义常量
const (
// 位数分配
sequenceBits = 12 // 序列号占用的位数
workerIdBits = 5 // 工作机器ID占用的位数
datacenterIdBits = 5 // 数据中心ID占用的位数
// 最大值
maxSequence = -1 ^ (-1 << sequenceBits) // 4095
maxWorkerId = -1 ^ (-1 << workerIdBits) // 31
maxDatacenterId = -1 ^ (-1 << datacenterIdBits) // 31
// 位移偏移量
workerIdShift = sequenceBits // 12
datacenterIdShift = sequenceBits + workerIdBits // 12 + 5 = 17
timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits // 12 + 5 + 5 = 22
)
// 起始时间戳 (纪元),可以使用程序上线的时间,这里设置为 2020-01-01 00:00:00 UTC
var epoch int64 = 1577836800000
// Snowflake 结构体
type Snowflake struct {
mu sync.Mutex // 互斥锁,保证并发安全
lastTime int64 // 上次生成ID的时间戳
workerId int64 // 工作机器ID
datacenterId int64 // 数据中心ID
sequence int64 // 当前毫秒内的序列号
}
// NewSnowflake 初始化一个 Snowflake 实例
// workerId: 工作机器ID (0 ~ 31)
// datacenterId: 数据中心ID (0 ~ 31)
func NewSnowflake(workerId, datacenterId int64) (*Snowflake, error) {
if workerId < 0 || workerId > maxWorkerId {
return nil, errors.New(fmt.Sprintf("worker Id can't be greater than %d or less than 0", maxWorkerId))
}
if datacenterId < 0 || datacenterId > maxDatacenterId {
return nil, errors.New(fmt.Sprintf("datacenter Id can't be greater than %d or less than 0", maxDatacenterId))
}
return &Snowflake{
lastTime: 0,
workerId: workerId,
datacenterId: datacenterId,
sequence: 0,
}, nil
}
// NextId 生成下一个 ID
func (s *Snowflake) NextId() (int64, error) {
s.mu.Lock()
defer s.mu.Unlock()
// 获取当前时间戳(毫秒)
now := time.Now().UnixMilli()
// 如果当前时间小于上次生成ID的时间说明时钟回拨抛出异常
if now < s.lastTime {
return 0, errors.New(fmt.Sprintf("Clock moved backwards. Refusing to generate id for %d milliseconds", s.lastTime-now))
}
// 如果是同一毫秒内
if now == s.lastTime {
// 序列号自增
s.sequence = (s.sequence + 1) & maxSequence
// 如果序列号溢出超过4095则等待下一毫秒
if s.sequence == 0 {
now = s.waitNextMillis(now)
}
} else {
// 不同毫秒序列号重置为0
s.sequence = 0
}
// 更新最后时间戳
s.lastTime = now
// 组装 ID
// (当前时间 - 起始时间) << 时间戳位移 | 数据中心ID << 数据中心位移 | 工作ID << 工作位移 | 序列号
id := ((now - epoch) << timestampLeftShift) |
(s.datacenterId << datacenterIdShift) |
(s.workerId << workerIdShift) |
s.sequence
return id, nil
}
// waitNextMillis 阻塞等待下一毫秒
func (s *Snowflake) waitNextMillis(lastTime int64) int64 {
now := time.Now().UnixMilli()
for now <= lastTime {
now = time.Now().UnixMilli()
}
return now
}
// ParseId 解析 ID用于调试或查看 ID 组成部分
func ParseId(id int64) map[string]interface{} {
timestamp := (id >> timestampLeftShift) + epoch
datacenterId := (id >> datacenterIdShift) & maxDatacenterId
workerId := (id >> workerIdShift) & maxWorkerId
sequence := id & maxSequence
return map[string]interface{}{
"id": id,
"timestamp": timestamp,
"time_str": time.UnixMilli(timestamp).Format("2006-01-02 15:04:05.000"),
"datacenterId": datacenterId,
"workerId": workerId,
"sequence": sequence,
}
}