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

82 lines
2.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package tools
import (
"errors"
"sync"
"time"
)
/*
* Snowflake
*
* 1 42 52 64
* +-----------------------------------------------+------------+---------------+
* | timestamp(ms) | workerid | sequence |
* +-----------------------------------------------+------------+---------------+
* | 0000000000 0000000000 0000000000 0000000000 0 | 0000000000 | 0000000000 00 |
* +-----------------------------------------------+------------+---------------+
*
* 1. 41位时间截(毫秒级),注意这是时间截的差值(当前时间截 - 开始时间截)。可以使用约70年: (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* 2. 10位数据机器位可以部署在1024个节点
* 3. 12位序列毫秒内的计数同一机器同一时间截并发4096个序号
*/
const (
twepoch = int64(1483228800000) //开始时间截 (2017-01-01)
workeridBits = uint(10) //机器id所占的位数
sequenceBits = uint(12) //序列所占的位数
workeridMax = int64(-1 ^ (-1 << workeridBits)) //支持的最大机器id数量
sequenceMask = int64(-1 ^ (-1 << sequenceBits)) //
workeridShift = sequenceBits //机器id左移位数
timestampShift = sequenceBits + workeridBits //时间戳左移位数
)
// A Snowflake struct holds the basic information needed for a snowflake generator worker
type Snowflake struct {
sync.Mutex
timestamp int64
workerid int64
sequence int64
}
// NewNode returns a new snowflake worker that can be used to generate snowflake IDs
func NewSnowflake(workerid int64) (*Snowflake, error) {
if workerid < 0 || workerid > workeridMax {
return nil, errors.New("workerid must be between 0 and 1023")
}
return &Snowflake{
timestamp: 0,
workerid: workerid,
sequence: 0,
}, nil
}
// Generate creates and returns a unique snowflake ID
func (s *Snowflake) Generate() int64 {
s.Lock()
now := time.Now().UnixNano() / 1000000
if s.timestamp == now {
s.sequence = (s.sequence + 1) & sequenceMask
if s.sequence == 0 {
for now <= s.timestamp {
now = time.Now().UnixNano() / 1000000
}
}
} else {
s.sequence = 0
}
s.timestamp = now
r := int64((now-twepoch)<<timestampShift | (s.workerid << workeridShift) | (s.sequence))
s.Unlock()
return r
}