zoukankan      html  css  js  c++  java
  • sonyflake.go

    // Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake.
    //第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
    // A Sonyflake ID is composed of
    //     39 bits for time in units of 10 msec
    //      8 bits for a sequence number
    //     16 bits for a machine id
    package sonyflake

    import (
        "errors"
        "net"
        "sync"
        "time"
    )

    // These constants are the bit lengths of Sonyflake ID parts.
    const (
        BitLenTime      = 39                               // bit length of time
        BitLenSequence  = 8                                // bit length of sequence number
        BitLenMachineID = 63 - BitLenTime - BitLenSequence // bit length of machine id
    )

    // Settings configures Sonyflake:
    //
    // StartTime is the time since which the Sonyflake time is defined as the elapsed time.
    // If StartTime is 0, the start time of the Sonyflake is set to "2014-09-01 00:00:00 +0000 UTC".
    // If StartTime is ahead of the current time, Sonyflake is not created.
    //
    // MachineID returns the unique ID of the Sonyflake instance.
    // If MachineID returns an error, Sonyflake is not created.
    // If MachineID is nil, default MachineID is used.
    // Default MachineID returns the lower 16 bits of the private IP address.
    //
    // CheckMachineID validates the uniqueness of the machine ID.
    // If CheckMachineID returns false, Sonyflake is not created.
    // If CheckMachineID is nil, no validation is done.
    type Settings struct {
        StartTime      time.Time
        MachineID      func() (uint16, error)
        CheckMachineID func(uint16) bool
    }

    // Sonyflake is a distributed unique ID generator.
    type Sonyflake struct {
        mutex       *sync.Mutex
        startTime   int64
        elapsedTime int64
        sequence    uint16
        machineID   uint16
    }

    // NewSonyflake returns a new Sonyflake configured with the given Settings.
    // NewSonyflake returns nil in the following cases:
    // - Settings.StartTime is ahead of the current time.
    // - Settings.MachineID returns an error.
    // - Settings.CheckMachineID returns false.
    func NewSonyflake(st Settings) *Sonyflake {
        sf := new(Sonyflake)
        sf.mutex = new(sync.Mutex)
        sf.sequence = uint16(1<<BitLenSequence - 1)

        if st.StartTime.After(time.Now()) {
            return nil
        }
        if st.StartTime.IsZero() {
            sf.startTime = toSonyflakeTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))
        } else {
            sf.startTime = toSonyflakeTime(st.StartTime)
        }

        var err error
        if st.MachineID == nil {
            sf.machineID, err = lower16BitPrivateIP()
        } else {
            sf.machineID, err = st.MachineID()
        }
        if err != nil || (st.CheckMachineID != nil && !st.CheckMachineID(sf.machineID)) {
            return nil
        }

        return sf
    }

    // NextID generates a next unique ID.
    // After the Sonyflake time overflows, NextID returns an error.
    func (sf *Sonyflake) NextID() (uint64, error) {
        const maskSequence = uint16(1<<BitLenSequence - 1)

        sf.mutex.Lock()
        defer sf.mutex.Unlock()

        current := currentElapsedTime(sf.startTime)
        if sf.elapsedTime < current {
            sf.elapsedTime = current
            sf.sequence = 0
        } else { // sf.elapsedTime >= current
            sf.sequence = (sf.sequence + 1) & maskSequence
            if sf.sequence == 0 {
                sf.elapsedTime++
                overtime := sf.elapsedTime - current
                time.Sleep(sleepTime((overtime)))
            }
        }

        return sf.toID()
    }

    const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec

    func toSonyflakeTime(t time.Time) int64 {
        return t.UTC().UnixNano() / sonyflakeTimeUnit
    }

    func currentElapsedTime(startTime int64) int64 {
        return toSonyflakeTime(time.Now()) - startTime
    }

    func sleepTime(overtime int64) time.Duration {
        return time.Duration(overtime)*10*time.Millisecond -
            time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit)*time.Nanosecond
    }

    func (sf *Sonyflake) toID() (uint64, error) {
        if sf.elapsedTime >= 1<<BitLenTime {
            return 0, errors.New("over the time limit")
        }

        return uint64(sf.elapsedTime)<<(BitLenSequence+BitLenMachineID) |
            uint64(sf.sequence)<<BitLenMachineID |
            uint64(sf.machineID), nil
    }

    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
    }

    // Decompose returns a set of Sonyflake ID parts.
    func Decompose(id uint64) map[string]uint64 {
        const maskSequence = uint64((1<<BitLenSequence - 1) << BitLenMachineID)
        const maskMachineID = uint64(1<<BitLenMachineID - 1)

        msb := id >> 63
        time := id >> (BitLenSequence + BitLenMachineID)
        sequence := id & maskSequence >> BitLenMachineID
        machineID := id & maskMachineID
        return map[string]uint64{
            "id":         id,
            "msb":        msb,
            "time":       time,
            "sequence":   sequence,
            "machine-id": machineID,
        }
    }

  • 相关阅读:
    MS SQL 事物日志传送能否跨数据库版本吗?
    MS SQL 模仿ORACLE的DESC
    Rhel-Server 5.5 安装ORACLE10
    ORACLE约束总结
    Win2003 设置远程连接限制数
    javascript学习代码-判断闰年
    javascript学习代码--点击按钮显示内容
    javascript学习代码
    反馈表样式
    调查表样式设计
  • 原文地址:https://www.cnblogs.com/zhangboyu/p/7462012.html
Copyright © 2011-2022 走看看