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

转载于:https://www.cnblogs.com/zhangboyu/p/7462012.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/416005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国内比较好用的5款测试管理工具

做好测试的前提是写好测试用例&#xff0c;写测试用例则需要一款好用的测试管理工具。国外有几款好用的测试管理工具&#xff0c;由于服务器部署在国外&#xff0c;国内访问会比较卡&#xff0c;还有就是语言不是中文的大家用起来也比较困难&#xff0c;这里就不推荐大家使用了…

如何设置Active Directory域控制器

安装第一个域控制器 请依次执行下列步骤&#xff0c;以便创建一个新的域&#xff0c;并在某一服务器上安装Active Directory服务&#xff0c;然后&#xff0c;将该服务器设定为对应于新建域的第一个域控制器&#xff1a; 在开始菜单上单击运行&#xff0c;并在随后出现的对话框…

mysql 转换编码方式

进入mysql 的安装文件夹找到 “ my.ini” 文件 &#xff08;mysql配置文件&#xff09; 一、编辑MySql的配置文件 vim /etc/my.cnf 在 [mysqld] 标签下加上三行 default-character-set utf8 character_set_server utf8 在 [mysql] 标签下加上一行 default-character-set ut…

mongodb报错 An error occurred while loading navigation: topology was destroyed

情况描述&#xff0c;关了电脑&#xff0c;第二天查询数据&#xff0c;报错An error occurred while loading navigation: topology was destroyed 我的数据库连接代码如下&#xff1a; var mongoose require(mongoose); mongoose.connect(mongodb://localhost/test); var d…

oracle常用命令收集

第一章&#xff1a;日志管理 1.forcing log switches sql> alter system switch logfile; 2.forcing checkpoints sql> alter system checkpoint; 3.adding online redo log groups sql> alter database add logfile [group 4] sql> (/disk3/log4a.rdo,/disk…

时间数组排序

时间数组&#xff1a; var timeArr ["2015-07-02","2016-01-01","2015-07-03","2015-12-02"] 时间数组从小到大排序: timeArr.sort((a,b)>{return parseInt(a.replace(/\D/g,),10) - parseInt(b.replace(/\D/g,),10) }) conosle.l…

zsh: command not found: nvm 解决方案

之前的nvm一直好用着&#xff0c;后来不知道为何&#xff0c;运行时候报错zsh: command not found: nvm 这是因为本机装了oh my zsh&#xff0c;而我之前nvm的配置是放在~/.bash_profile里面。 我选择了按照官方文档重新装一下&#xff0c;解决问题. 下载安装 curl -o- https:…

集合替换元素

排序比较简单&#xff0c;简略说一下 [java] view plaincopy print?ArrayList nums new ArrayList(); nums.add(.....) ...... Collections.reverse(nums); //次序反转 Collections.sort(nums); //按自然顺序排序 Collections.shuffle(nums); //随机排序 查找&am…

[05] Session概要

1、Session是什么除了使用Cookie&#xff0c;Web应用程序中还经常使用Session来记录客户端状态&#xff0c;即Session是服务器端使用的一种保存客户端状态的机制。Cookie在客户端&#xff0c;Session在服务器端。围绕以上的概念来说&#xff0c;其实Session还包含不同的语义&am…

我的学习网站

看到的实用资料记录网址&#xff1a; 1、技术学习网站&#xff1a; http://www.ixpub.net/forum.php 2、禅道项目管理软件(ZenTaoPMS)是一款国产的&#xff0c;基于LGPL协议&#xff0c;开源免费的项目管理软件&#xff0c;它集产品管理、项目管理、测试管理于一体&#xff0c;…

我对组件components 和 页面 views 的思考

之前做过很多项目&#xff0c;我喜欢把组件放到components目录中、页面入口文件放到views中&#xff0c;但哪些文件需要放到components中&#xff0c;哪些文件需要放到views中呢&#xff0c;我现在发现&#xff0c;在自己做很多个项目时&#xff0c;需要尽可能复用组件&#xf…

Android 7.0 获取相机拍照图片,适配三星手机拍照,解决三星手机拍照屏幕旋转,判断设备是否有摄像头

方法1 新建/res/xml/file_paths: <?xml version"1.0" encoding"utf-8"?> <paths xmlns:android"http://schemas.android.com/apk/res/android"><external-path name"external_files" path"."/> </p…

UVA - 10079 Pizza Cutting

/* 主要是画图&#xff0c;画很多图&#xff0c;找规律&#xff0c;找到规律以后&#xff0c;发现算是简单题思路的关键是&#xff1a;每次切割都与前(i-1)刀有交点的情况下&#xff0c;得到的块数是最大的 */ #include <iostream> typedef long long ll; using nam…

Win XP必须禁止的服务

1.NetMeeting Remote Desktop Sharing&#xff1a;允许受权的用户通过NetMeeting在网络上互相访问对方。这项服务对大多数个人用户并没有多大用处&#xff0c;况且服务的开启还会带来安全问题&#xff0c;因为上网时该服务会把用户名以明文形式发送到连接它的客户端&#xff0c…

nuxt2.0 设置 webpack 路径别名

如果想在nuxt中直接使用其他文件的路径&#xff0c;比如下面的components&#xff0c;我们需要在nuxt.config.js进行配置即可。 import SiteHeader from components/site/SiteHeader.vuebuild: {/*** You can extend webpack config here*/extend(config, ctx) {// Run ESLint…