竹简文档
雪花算法

节点管理

Node 结构体管理雪花算法节点,支持自动和手动配置

节点管理

Node 结构体是雪花算法的核心,负责生成唯一 ID。每个节点由数据中心 ID 和节点 ID 唯一标识。

Node 结构体

snowflake.go
type Node struct {
    mu           sync.Mutex  // 互斥锁,保证线程安全
    datacenterID int64       // 数据中心 ID (0-7)
    nodeID       int64       // 节点 ID (0-7)
    lastTime     int64       // 上次生成 ID 的时间戳
    sequence     int64       // 当前毫秒内的序列号
}

创建节点

手动创建

snowflake.go
func NewNode(datacenterID, nodeID int64) (*Node, error)

参数范围:

参数范围说明
datacenterID0-7数据中心 ID
nodeID0-7节点 ID

示例:

// 创建数据中心 1,节点 2 的节点
node, err := xSnowflake.NewNode(1, 2)
if err != nil {
    panic(err)
}

// 使用节点生成 ID
id := node.MustGenerate(xSnowflake.GeneUser)

默认节点

框架提供全局默认节点,自动初始化:

global.go
// 初始化默认节点
func InitDefaultNode() error

// 获取默认节点(自动初始化)
func GetDefaultNode() *Node

// 使用默认节点生成 ID
func GenerateID(gene ...Gene) SnowflakeID

示例:

// 方式一:直接使用全局函数
id := xSnowflake.GenerateID(xSnowflake.GeneUser)

// 方式二:获取默认节点后使用
node := xSnowflake.GetDefaultNode()
id := node.MustGenerate(xSnowflake.GeneUser)

节点 ID 获取逻辑

默认节点的 ID 获取优先级:

1. 环境变量(优先)

.env
SNOWFLAKE_DATACENTER_ID=1
SNOWFLAKE_NODE_ID=2

2. 自动生成(回退)

未配置环境变量时,自动生成节点 ID:

global.go
func autoGenerateIDs() (datacenterID, nodeID int64) {
    var hashInput []byte

    // 优先使用 MAC 地址
    interfaces, err := net.Interfaces()
    if err == nil {
        for _, iface := range interfaces {
            if iface.Flags&net.FlagLoopback != 0 || len(iface.HardwareAddr) < 6 {
                continue
            }
            hashInput = iface.HardwareAddr
            break
        }
    }

    // 回退到主机名
    if len(hashInput) == 0 {
        hostname, _ := os.Hostname()
        if hostname == "" {
            hostname = "unknown-host"
        }
        hashInput = []byte(hostname)
    }

    // 使用 FNV-1a 哈希算法
    h := fnv.New64a()
    _, _ = h.Write(hashInput)
    hash := h.Sum64()

    // 从哈希值中提取 ID
    datacenterID = int64(hash % uint64(maxDatacenterID+1))
    nodeID = int64((hash >> 3) % uint64(maxNodeID+1))

    return
}

特点:

  • 同一台机器重启后获得相同的节点 ID
  • 不同机器大概率获得不同的节点 ID
  • 无需手动配置,开箱即用

Node 方法

Generate

生成 ID,线程安全:

snowflake.go
func (n *Node) Generate(gene ...Gene) (SnowflakeID, error)

示例:

node, _ := xSnowflake.NewNode(1, 1)

// 生成普通 ID
id, err := node.Generate()

// 生成带基因的 ID
id, err := node.Generate(xSnowflake.GeneUser)

MustGenerate

生成 ID,失败则 panic:

snowflake.go
func (n *Node) MustGenerate(gene ...Gene) SnowflakeID

示例:

id := node.MustGenerate(xSnowflake.GeneOrder)

获取节点信息

snowflake.go
func (n *Node) DatacenterID() int64

func (n *Node) NodeID() int64

示例:

node := xSnowflake.GetDefaultNode()

fmt.Println("数据中心:", node.DatacenterID()) // 1
fmt.Println("节点:", node.NodeID())          // 2

ID 生成核心逻辑

snowflake.go
func (n *Node) Generate(gene ...Gene) (SnowflakeID, error) {
    g := GeneDefault
    if len(gene) > 0 {
        g = gene[0]
    }

    if !g.IsValid() {
        return 0, fmt.Errorf("基因类型必须在 0-%d 之间", maxGene)
    }

    n.mu.Lock()
    defer n.mu.Unlock()

    now := time.Now().UnixMilli()

    if now == n.lastTime {
        // 同一毫秒内,序列号递增
        n.sequence = (n.sequence + 1) & maxSequence
        if n.sequence == 0 {
            // 序列号用尽,等待下一毫秒
            for now <= n.lastTime {
                now = time.Now().UnixMilli()
            }
        }
    } else {
        // 新的毫秒,序列号重置
        n.sequence = 0
    }

    n.lastTime = now

    // 组装 ID
    id := ((now - epoch) << timestampShift) |
        (int64(g) << geneShift) |
        (n.datacenterID << datacenterShift) |
        (n.nodeID << nodeShift) |
        n.sequence

    return SnowflakeID(id), nil
}

分布式部署

在分布式环境中,确保不同节点配置不同的 ID:

方式一:环境变量

docker-compose.yml
services:
  app-1:
    environment:
      - SNOWFLAKE_DATACENTER_ID=1
      - SNOWFLAKE_NODE_ID=1

  app-2:
    environment:
      - SNOWFLAKE_DATACENTER_ID=1
      - SNOWFLAKE_NODE_ID=2

  app-3:
    environment:
      - SNOWFLAKE_DATACENTER_ID=2
      - SNOWFLAKE_NODE_ID=1

方式二:自动生成

不配置环境变量,依赖 MAC 地址自动生成:

// 每台机器自动获得唯一的节点 ID
reg := xReg.Register()

下一步

On this page