// 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,
    }
}

sonyflake.go的更多相关文章

  1. 雪花算法 Snowflake & Sonyflake

    唯一ID算法Snowflake相信大家都不墨生,他是Twitter公司提出来的算法.非常广泛的应用在各种业务系统里.也因为Snowflake的灵活性和缺点,对他的改造层出不穷,比百度的UidGener ...

  2. Go 语言相关的优秀框架,库及软件列表

    If you see a package or project here that is no longer maintained or is not a good fit, please submi ...

  3. 15. Go 语言“避坑”与技巧

    Go 语言"避坑"与技巧 任何编程语言都不是完美的,Go 语言也是如此.Go 语言的某些特性在使用时如果不注意,也会造成一些错误,我们习惯上将这些造成错误的设计称为"坑& ...

  4. Awesome Go精选的Go框架,库和软件的精选清单.A curated list of awesome Go frameworks, libraries and software

    Awesome Go      financial support to Awesome Go A curated list of awesome Go frameworks, libraries a ...

  5. go语言实现分布式id生成器

    本文:https://chai2010.cn/advanced-go-programming-book/ch6-cloud/ch6-01-dist-id.html 分布式id生成器 有时我们需要能够生 ...

  6. snowflake 雪花算法 分布式实现全局id生成

    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID. 这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案 ...

  7. go多种uuid生成方式

    package main import ( "fmt" "github.com/chilts/sid" "github.com/kjk/betterg ...

  8. 分布式ID生成器及redis,etcd分布式锁

    分布式id生成器 有时我们需要能够生成类似MySQL自增ID这样不断增大,同时又不会重复的id.以支持业务中的高并发场景.比较典型的,电商促销时,短时间内会有大量的订单涌入到系统,比如每秒10w+.明 ...

随机推荐

  1. 云技术:弹性计算ECS

    云计算(Cloud Computing)被业界看作继大型计算机.个人计算机.互联网之后的第四次IT产业革命,正日益成为未来互联网与移动技术相结合的一种新兴计算模式.云计算提供了IT基础设施和平台服务的 ...

  2. https认证

    HTTPS认证 说明 1. HTTPS协议的站点信息更加安全,同时可降低网站被劫持的风险,如网站同时存在HTTP和HTTPS站点,可使用本工具进行认证,便于百度搜索识别网站HTTP与HTTPS之间的对 ...

  3. LeetCode - 二叉树的最大深度

    自己解法,欢迎拍砖 给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说明: 叶子节点是指没有子节点的节点. 示例:给定二叉树 [3,9,20,null,nu ...

  4. webpack.config.js配置文件

    1.基本配置 webpack在执行时,除在命令行传入参数,还可以通过指定的配置文件来执行.默认会搜索当前目录下webpack.config.js.这个文件是一个node.js模块,返回一个json格式 ...

  5. 用nodejs实现简单爬虫

    前言   本喵最近工作中需要使用node,并也想晋升为全栈工程师,所以开始了node学习之旅,在学习过程中, 我会总结一些实用的例子,做成博文和视频教程,以实例形式来理解体会node的用法,所以跟小猫 ...

  6. Webpack vs Browersify vs SystemJs for SPAs

    https://engineering.velocityapp.com/webpack-vs-browersify-vs-systemjs-for-spas-95b349a41fa0 Right no ...

  7. HDU-5705

    Clock Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Problem De ...

  8. jdbc连接阿里云服务器上的MySQL数据库 及 数据库IP限制

    问题1:Jdbc 如何连接阿里云服务器上的MySQL数据库? 解决: 上截图: 其中IP是阿里云服务器的公网IP地址. 问题2:   刚开始接手开发的时候,使用Navicat连接阿里云服务器上的数据后 ...

  9. 寻找DevExpress破解经历之旅

    众所周知DevExpress是收费的,但是破解版的也不少,近期公司需要做发票套打的功能让我找个打印工具,我寻思着DevExpress这个软件好像挺不错的,功能强大,看了下价格方面,好吧!2W多呢,市面 ...

  10. HTML编码和CSS编码会遇到的问

    http://codeguide.bootcss.com/#html-syntax  参考链接 属性顺序 HTML 属性应当按照以下给出的顺序依次排列,确保代码的易读性. class id, name ...