From 55ca29a22e9cd071af05be9db571f3d201e74202 Mon Sep 17 00:00:00 2001 From: 1 <1@72wo.cn> Date: Mon, 22 Sep 2025 14:44:02 +0000 Subject: [PATCH] =?UTF-8?q?refactor(utils):=20=E7=A7=BB=E9=99=A4=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E9=9B=AA=E8=8A=B1=E7=AE=97=E6=B3=95=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=EF=BC=8C=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?Snowflake=E7=94=9F=E6=88=90=E5=99=A8=E5=B9=B6=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?cronex=E9=80=92=E5=BD=92=E8=B0=83=E7=94=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/cool/global.go | 17 ++-- common/utils/cronex/cronex.go | 4 +- common/utils/snowflake.go | 149 ++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 common/utils/snowflake.go diff --git a/common/cool/global.go b/common/cool/global.go index 58cecf47a..1f027a31f 100644 --- a/common/cool/global.go +++ b/common/cool/global.go @@ -7,8 +7,6 @@ import ( "github.com/antlabs/cronex" "github.com/gogf/gf/v2/os/glog" - "github.com/gogf/gf/v2/util/gconv" - "github.com/yitter/idgenerator-go/idgen" sensitive "github.com/zmexing/go-sensitive-word" ) @@ -27,17 +25,20 @@ func init() { // 创建 IdGeneratorOptions 对象,可在构造函数中输入 WorkerId: Loger.SetFlags(glog.F_TIME_STD | glog.F_FILE_LONG) //设置flag Loger.Print(ctx, "初始化日志") - tt := gconv.Uint16(1) - var options = idgen.NewIdGeneratorOptions(tt) + // tt := gconv.Uint16(1) + // var options = idgen.NewIdGeneratorOptions(tt) + // options.SeqBitLength = 4 // options.WorkerIdBitLength = 10 // 默认值6,限定 WorkerId 最大值为2^6-1,即默认最多支持64个节点。 // options.SeqBitLength = 6; // 默认值6,限制每毫秒生成的ID个数。若生成速度超过5万个/秒,建议加大 SeqBitLength 到 10。 // options.BaseTime = Your_Base_Time // 如果要兼容老系统的雪花算法,此处应设置为老系统的BaseTime。 // ...... 其它参数参考 IdGeneratorOptions 定义。 - // 保存参数(务必调用,否则参数设置不生效): - idgen.SetIdGenerator(options) - newId := idgen.NextId() - glog.Debug(context.Background(), "初始化雪花算法", newId) + // // 保存参数(务必调用,否则参数设置不生效): + // idgen.SetIdGenerator(options) + // newId := idgen.NextId() + // glog.Debug(context.Background(), "初始化雪花算法", newId) + glog.Debug(context.Background(), "初始化雪花算法", utils.DefaultGenerator.Get()) + Filter, _ = sensitive.NewFilter( sensitive.StoreOption{Type: sensitive.StoreMemory}, // 基于内存 sensitive.FilterOption{Type: sensitive.FilterDfa}, // 基于DFA算法 diff --git a/common/utils/cronex/cronex.go b/common/utils/cronex/cronex.go index c03711604..74d0fcbb7 100644 --- a/common/utils/cronex/cronex.go +++ b/common/utils/cronex/cronex.go @@ -37,10 +37,10 @@ func (c *Cronex) AddFunc(spec string, cmd func()) (node TimerNoder, err error) { // 运行消费者循环 func (c *Cronex) Run() { - c.Run() + c.Timer.Run() } // 关闭cronex的任务循环 func (c *Cronex) Stop() { - c.Stop() + c.Timer.Stop() } diff --git a/common/utils/snowflake.go b/common/utils/snowflake.go new file mode 100644 index 000000000..39e3296ba --- /dev/null +++ b/common/utils/snowflake.go @@ -0,0 +1,149 @@ +package utils + +import ( + "errors" + "strconv" + "sync" + "time" +) + +// Snowflake is a uint32-sized ID with 32 node support and 8.5 year lifespan +type Snowflake uint32 + +// ID is an alias for Snowflake +type ID = Snowflake + +func (s Snowflake) String() string { + return strconv.FormatUint(uint64(s), 16) +} + +// ParseSnowflake parses a string into a snowflake, if possible +func ParseSnowflake(s string) (Snowflake, error) { + i, err := strconv.ParseUint(s, 16, 32) + if err != nil { + return 0, err + } + return Snowflake(i), nil +} + +// DefaultGenerator is a Generator with the epoch set to Jan 1, 2025 UTC +var DefaultGenerator = NewGen(time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC), 0) + +// Generator holds info needed for generating snowflakes with 32 node support +type Generator struct { + Epoch time.Time + nodeID uint8 // 0-31 (5位,支持32个节点) + sequence uint8 // 0-255 (8位,支持每秒256个ID) + lastSec uint32 // 上次生成ID的秒级时间戳 + mu sync.Mutex +} + +// NewGen returns a new generator with specified epoch and node ID +func NewGen(epoch time.Time, nodeID uint8) *Generator { + if nodeID >= 32 { // 5位最多支持0-31 + panic(errors.New("node ID must be between 0 and 31")) + } + return &Generator{ + Epoch: epoch, + nodeID: nodeID, + } +} + +// Get generates a new unique snowflake ID and returns it as uint32 +func (g *Generator) Get() uint32 { + g.mu.Lock() + defer g.mu.Unlock() + + // 获取当前时间戳(自纪元以来的秒数) + now := time.Now() + sec := uint32(now.Sub(g.Epoch).Seconds()) + + // 检查时间戳是否超出28位限制 (2^28 - 1 = 268435455秒 ≈ 8.5年) + if sec >= (1 << 28) { + panic(errors.New("timestamp overflow: please update epoch")) + } + + // 处理时间回拨 + if sec < g.lastSec { + // 计算需要等待的时间 + sleepDuration := time.Duration(g.lastSec-sec) * time.Second + // 添加1毫秒缓冲,确保时间确实已过 + sleepDuration += time.Millisecond + // 等待时间追上 + time.Sleep(sleepDuration) + + // 重新获取时间戳 + now = time.Now() + sec = uint32(now.Sub(g.Epoch).Seconds()) + + // 再次检查,防止极端情况 + if sec < g.lastSec { + panic(errors.New("clock moved backwards beyond recovery")) + } + } + + // 处理同一秒内的序列号 + if sec == g.lastSec { + g.sequence++ + // 序列号溢出(8位最大255) + if g.sequence > 255 { // 0-255是有效范围 + // 等待到下一秒 + sleepDuration := time.Second - time.Duration(now.Nanosecond())*time.Nanosecond + time.Microsecond + time.Sleep(sleepDuration) + + now = time.Now() + sec = uint32(now.Sub(g.Epoch).Seconds()) + g.sequence = 0 + } + } else { + // 新的一秒,重置序列号 + g.sequence = 0 + } + + // 更新上次时间戳 + g.lastSec = sec + + // 组装ID (32位): + // [28位秒级时间戳][5位节点ID][8位序列号] + var id uint32 + id |= (sec << 13) // 左移13位(5+8)放置秒级时间戳 + id |= (uint32(g.nodeID) << 8) // 左移8位放置节点ID + id |= uint32(g.sequence) // 放置序列号 + + return id +} + +// Parse extracts timestamp, node ID and sequence from a snowflake +func (g *Generator) Parse(id uint32) (t time.Time, nodeID uint8, sequence uint8) { + // 提取各部分信息 + sec := id >> 13 + nodeID = uint8((id >> 8) & 0x1F) // 5位掩码(0x1F = 31) + sequence = uint8(id & 0xFF) // 8位掩码(0xFF = 255) + + // 计算实际时间 + totalNanos := g.Epoch.UnixNano() + int64(sec)*int64(time.Second) + t = time.Unix(0, totalNanos).UTC() + return +} + +// GetNodeID returns the current node ID +func (g *Generator) GetNodeID() uint8 { + return g.nodeID +} + +// GetLastTimestamp returns the last timestamp used (for testing/debugging) +func (g *Generator) GetLastTimestamp() uint32 { + g.mu.Lock() + defer g.mu.Unlock() + return g.lastSec +} + +// MaxAge returns the maximum age (in seconds) that this generator can handle before overflow +func (g *Generator) MaxAge() uint32 { + return (1 << 28) - 1 // 268435455秒,约8.5年 +} + +// MaxSequencePerSecond returns the maximum number of sequences per second +func (g *Generator) MaxSequencePerSecond() uint8 { + return 255 // 2^8 - 1 +}