add snowflakke
This commit is contained in:
commit
f063a54fbc
67
machine-id/machine-id.go
Normal file
67
machine-id/machine-id.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 用于获取MachineID,MachineID指当前机器的id,使用机器第一张网卡的可用ip地址构造
|
||||||
|
// 提供全局变量
|
||||||
|
// `MachineIDStr string`用于保存MachineID的字符串形式
|
||||||
|
// `MachineID uint16`用于保存MachineID原始的整型数形式
|
||||||
|
// 这两个全局变量都会在模块初始化时计算获得,不需要额外调用函数获得
|
||||||
|
package machineid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func privateIPv4() (net.IP, error) {
|
||||||
|
as, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range as {
|
||||||
|
ipnet, ok := a.(*net.IPNet)
|
||||||
|
if !ok || ipnet.IP.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := ipnet.IP.To4()
|
||||||
|
if isPrivateIPv4(ip) {
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("no private ip address")
|
||||||
|
}
|
||||||
|
func isPrivateIPv4(ip net.IP) bool {
|
||||||
|
return ip != nil &&
|
||||||
|
(ip[0] == 10 || ip[0] == 172 && (ip[1] >= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lower16BitPrivateIP() (uint16, error) {
|
||||||
|
ip, err := privateIPv4()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint16(ip[2])<<8 + uint16(ip[3]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MachineID 当前机器的id
|
||||||
|
var MachineID uint16 = 0
|
||||||
|
|
||||||
|
// MachineIDStr 当前机器id的字符串形式
|
||||||
|
var MachineIDStr string
|
||||||
|
|
||||||
|
// getMachineID 获取生成器的MachineID
|
||||||
|
func getMachineID() string {
|
||||||
|
if MachineID == 0 {
|
||||||
|
mID, err := lower16BitPrivateIP()
|
||||||
|
if err != nil {
|
||||||
|
return strconv.FormatUint(uint64(MachineID), 16)
|
||||||
|
}
|
||||||
|
MachineID = mID
|
||||||
|
}
|
||||||
|
return strconv.FormatUint(uint64(MachineID), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
MachineIDStr = getMachineID()
|
||||||
|
}
|
124
snowflake/snowflake.go
Normal file
124
snowflake/snowflake.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package snowflake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
epoch = 1491696000000
|
||||||
|
serverBits = 10
|
||||||
|
sequenceBits = 12
|
||||||
|
timeBits = 42
|
||||||
|
serverShift = sequenceBits
|
||||||
|
timeShift = sequenceBits + serverBits
|
||||||
|
serverMax = ^(-1 << serverBits)
|
||||||
|
sequenceMask = ^(-1 << sequenceBits)
|
||||||
|
timeMask = ^(-1 << timeBits)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Generator struct {
|
||||||
|
state uint64
|
||||||
|
machine uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(machineID int) *Generator {
|
||||||
|
if machineID < 0 || machineID > serverMax {
|
||||||
|
panic(fmt.Errorf("invalid machine id; must be 0 ≤ id < %d", serverMax))
|
||||||
|
}
|
||||||
|
return &Generator{
|
||||||
|
state: 0,
|
||||||
|
machine: uint64(machineID << serverShift),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) MachineID() int {
|
||||||
|
return int(g.machine >> serverShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) Next() uint64 {
|
||||||
|
var state uint64
|
||||||
|
|
||||||
|
// we attempt 100 times to update the millisecond part of the state
|
||||||
|
// and increment the sequence atomically. each attempt is approx ~30ns
|
||||||
|
// so we spend around ~3µs total.
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
t := (now() - epoch) & timeMask
|
||||||
|
current := atomic.LoadUint64(&g.state)
|
||||||
|
currentTime := current >> timeShift & timeMask
|
||||||
|
currentSeq := current & sequenceMask
|
||||||
|
|
||||||
|
// this sequence of conditionals ensures a monotonically increasing
|
||||||
|
// state.
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// if our time is in the future, use that with a zero sequence number.
|
||||||
|
case t > currentTime:
|
||||||
|
state = t << timeShift
|
||||||
|
|
||||||
|
// we now know that our time is at or before the current time.
|
||||||
|
// if we're at the maximum sequence, bump to the next millisecond
|
||||||
|
case currentSeq == sequenceMask:
|
||||||
|
state = (currentTime + 1) << timeShift
|
||||||
|
|
||||||
|
// otherwise, increment the sequence.
|
||||||
|
default:
|
||||||
|
state = current + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if atomic.CompareAndSwapUint64(&g.state, current, state) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
state = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// since we failed 100 times, there's high contention. bail out of the
|
||||||
|
// loop to bound the time we'll spend in this method, and just add
|
||||||
|
// one to the counter. this can cause millisecond drift, but hopefully
|
||||||
|
// some CAS eventually succeeds and fixes the milliseconds. additionally,
|
||||||
|
// if the sequence is already at the maximum, adding 1 here can cause
|
||||||
|
// it to roll over into the machine id. giving the CAS 100 attempts
|
||||||
|
// helps to avoid these problems.
|
||||||
|
if state == 0 {
|
||||||
|
state = atomic.AddUint64(&g.state, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state | g.machine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) NextString() string {
|
||||||
|
var s [11]byte
|
||||||
|
encode(&s, g.Next())
|
||||||
|
return string(s[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) AppendNext(s *[11]byte) {
|
||||||
|
encode(s, g.Next())
|
||||||
|
}
|
||||||
|
|
||||||
|
func now() uint64 { return uint64(time.Now().UnixNano() / 1e6) }
|
||||||
|
|
||||||
|
var digits = [...]byte{
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||||
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||||
|
'U', 'V', 'W', 'X', 'Y', 'Z', '_', 'a', 'b', 'c',
|
||||||
|
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
||||||
|
'x', 'y', 'z', '~'}
|
||||||
|
|
||||||
|
func encode(s *[11]byte, n uint64) {
|
||||||
|
s[10], n = digits[n&0x3f], n>>6
|
||||||
|
s[9], n = digits[n&0x3f], n>>6
|
||||||
|
s[8], n = digits[n&0x3f], n>>6
|
||||||
|
s[7], n = digits[n&0x3f], n>>6
|
||||||
|
s[6], n = digits[n&0x3f], n>>6
|
||||||
|
s[5], n = digits[n&0x3f], n>>6
|
||||||
|
s[4], n = digits[n&0x3f], n>>6
|
||||||
|
s[3], n = digits[n&0x3f], n>>6
|
||||||
|
s[2], n = digits[n&0x3f], n>>6
|
||||||
|
s[1], n = digits[n&0x3f], n>>6
|
||||||
|
s[0] = digits[n&0x3f]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user