package main

import (
"errors"
"fmt"
"strconv"
"sync"
"time"
) /*
* 算法解释
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下的epoch属性)。
* 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
const (
//t := time.Date(2015, 1, 1, 00, 00, 00, 00, time.Local).UnixNano() / 1e6;//获取时间戳 毫秒
//开始时间戳 2015-1-1
epoch int64 = 1420041600000
// 机器id所占的位数
workerIdBits int64 = 5
// 数据标识id所占的位数
datacenterIdBits int64 = 5
//支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
maxWorkerId int64 = -1 ^ (-1 << workerIdBits)
// 支持的最大数据标识id,结果是31
maxDatacenterId int64 = -1 ^ (-1 << datacenterIdBits)
//序列在id中占的位数
sequenceBits int64 = 12
// 机器ID向左移12位
workerIdShift int64 = sequenceBits;
// 数据标识id向左移17位(12+5)
datacenterIdShift int64 = sequenceBits + workerIdBits;
// 时间截向左移22位(5+5+12)
timestampLeftShift int64 = sequenceBits + workerIdBits + datacenterIdBits
// 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
sequenceMask int64 = -1 ^ (-1 << sequenceBits)
) /*
* 构造
*/
type SnowflakeIdWorker struct {
mutex sync.Mutex // 添加互斥锁 确保并发安全
lastTimestamp int64 // 上次生成ID的时间截
workerId int64 // 工作机器ID(0~31)
datacenterId int64 //数据中心ID(0~31)
sequence int64 // 毫秒内序列(0~4095)
} /*
* 创建SnowflakeIdWorker
* workerId 工作ID (0~31)
* datacenterId 数据中心ID (0~31)
*/
func createWorker(wId int64,dId int64)(*SnowflakeIdWorker,error){
if wId < 0 || wId > maxWorkerId {
return nil, errors.New("Worker ID excess of quantity")
}
if dId < 0 || dId > maxDatacenterId {
return nil, errors.New("Datacenter ID excess of quantity")
}
// 生成一个新节点
return &SnowflakeIdWorker{
lastTimestamp: 0,
workerId: wId,
datacenterId: dId,
sequence: 0,
}, nil
} /*
* 获取ID
*/
func (w *SnowflakeIdWorker) nextId() int64 {
// 保障线程安全 加锁
w.mutex.Lock()
// 生成完成后 解锁
defer w.mutex.Unlock()
// 获取生成时的时间戳 毫秒
now := time.Now().UnixNano() / 1e6
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if now < w.lastTimestamp {
errors.New("Clock moved backwards")
//根据需要自定义错误码
return 3001
}
if w.lastTimestamp == now {
w.sequence = (w.sequence + 1) & sequenceMask
if w.sequence == 0 {
// 阻塞到下一个毫秒,直到获得新的时间戳
for now <= w.lastTimestamp {
now = time.Now().UnixNano() / 1e6
}
}
}else {
// 当前时间与工作节点上一次生成ID的时间不一致 则需要重置工作节点生成ID的序号
w.sequence = 0
}
// 将机器上一次生成ID的时间更新为当前时间
w.lastTimestamp = now
ID := int64((now - epoch) << timestampLeftShift | w.datacenterId << datacenterIdShift | (w.workerId << workerIdShift) | w.sequence)
return ID
} /*
* 将十进制数字转化为二进制字符串
*/
func convertToBin(num int64) string {
s := ""
if num == 0 {
return "0"
}
// num /= 2 每次循环的时候 都将num除以2 再把结果赋值给 num
for ;num > 0 ; num /= 2 {
lsb := num % 2
// 将数字强制性转化为字符串
s = strconv.FormatInt(lsb,10) + s
}
return s
} func main() {
worker,err := createWorker(0,0)
if err != nil {
fmt.Println(err)
return
}
ch := make(chan int64)
count := 100
// 并发 goroutine ID生成
for i := 0; i < count; i++ {
go func() {
id := worker.nextId()
ch <- id
}()
}
defer close(ch)
m := make(map[int64]int)
for i := 0; i < count; i++ {
id := <- ch
// map中存在为id的key,说明生成的 ID有重复
_, ok := m[id]
if ok {
fmt.Println("ID is not unique!")
}
// id作为key存入map
m[id] = i
fmt.Println(id)
fmt.Println(convertToBin(id))
}
}

go实现SnowFlake的更多相关文章

  1. 关于全局ID,雪花(snowflake)算法的说明

    上次简单的说一下:http://www.cnblogs.com/dunitian/p/6041745.html#uid C#版本的国外朋友已经封装了,大家可以去看看:https://github.co ...

  2. Snowflake 全局唯一Id 生成

    /// <summary> /// From: https://github.com/twitter/snowflake /// An object that generates IDs. ...

  3. POJ 3349 Snowflake Snow Snowflakes(简单哈希)

    Snowflake Snow Snowflakes Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 39324   Accep ...

  4. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  5. Twitter Snowflake 的Java实现

    在关闭显示的情况下, 可以达到每毫秒3万个的生成速度 /** * An Implementation of Twitter Snowflake ID Generator */ public class ...

  6. C# 实现 Snowflake算法 ID生成

    http://blog.csdn.net/w200221626/article/details/52064976 C# 实现 Snowflake算法 /// <summary> /// 动 ...

  7. 【hiho一下第77周】递归-减而治之 (MS面试题:Koch Snowflake)

    本题是一道微软面试题,看起来复杂,解出来会发现其实是一个很简单的递归问题,但是这道题的递归思路是很值得我们反复推敲的. 原题为hihocoder第77周的题目. 描述 Koch Snowflake i ...

  8. Snowflake Snow Snowflakes(哈希表的应用)

    Snowflake Snow Snowflakes Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 27312   Accep ...

  9. poj 3349:Snowflake Snow Snowflakes(哈希查找,求和取余法+拉链法)

    Snowflake Snow Snowflakes Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 30529   Accep ...

  10. PHP使用SnowFlake算法生成唯一ID

    前言:最近需要做一套CMS系统,由于功能比较单一,而且要求灵活,所以放弃了WP这样的成熟系统,自己做一套相对简单一点的.文章的详情页URL想要做成url伪静态的格式即xxx.html 其中xxx考虑过 ...

随机推荐

  1. day18作业

    作业: # 1.编写课上讲解的有参装饰器准备明天默写 def auth(file_type): def outer(func): def inter(*args,**kwargs): if file_ ...

  2. sqli-labs通关教程----21~30关

    第二十一关 第二十一关我们正常登陆后看到,uname后面变成了一堆字母 这是经过base64编码之后的样子,所以就照葫芦画瓢,将我payload的uname后面的部分转码成base64,这里可以用正常 ...

  3. redis 分布式锁的 5个坑,真是又大又深

    引言 最近项目上线的频率颇高,连着几天加班熬夜,身体有点吃不消精神也有些萎靡,无奈业务方催的紧,工期就在眼前只能硬着头皮上了.脑子浑浑噩噩的时候,写的就不能叫代码,可以直接叫做Bug.我就熬夜写了一个 ...

  4. 性能测试-pidstat 问题定位分析

    pidstat 概述 pidstat是sysstat工具的一个命令,用于监控全部或指定进程的cpu.内存.线程.设备IO等系统资源的占用情况.pidstat首次运行时显示自系统启动开始的各项统计信息, ...

  5. PHP代码审计理解(二)----齐博CMS7.0文件覆盖

    0x00 前言 因为我是跟着视频操作的,这回真的没理解为什么定位到了这个存在漏洞的文件... /do/fujsarticle.php 因为没有前文,所以这里无法分析这个$FileName为什么可以$_ ...

  6. golang实现并发爬虫一(单任务版本爬虫功能)

    目的是写一个golang并发爬虫版本的演化过程. 那么在演化之前,当然是先跑通一下单任务版本的架构. 正如人走路之前是一定要学会爬走一般. 首先看一下单任务版本的爬虫架构,如下: 这是单任务版本爬虫的 ...

  7. python os模块获取指定目录下的文件列表

    bath_path = r"I:\ner_results\ner_results" dir_list1 = os.listdir(bath_path) for dir1 in di ...

  8. 理解java容器底层原理--手动实现LinkedList

    Node java 中的 LIinkedList 的数据结构是链表,而链表中每一个元素是节点. 我们先定义一下节点: package com.xzlf.collection; public class ...

  9. centos 7 安装更新php5.6

    epel  remi 什么的把我弄晕了 不深研这东西了,直接按步骤操作更新了. # yum install epel-release # rpm -ivh http://rpms.famillecol ...

  10. 2019-2020-1 20199326《Linux内核原理与分析》第七周作业

    实验内容:分析Linux内核创建一个新进程的过程 初始化Menu Os,输入fork可以看到menuos触发了一个fork系统调用 再开一个shell,进入调试模式,设置几个断点sys_clone,d ...