import bigInt from 'big-integer';
// http://peterolson.github.io/BigInteger.js/
/**
* Twitter_Snowflake(雪花算法)
*
* SnowFlake的结构如下(共64bits,每部分用-分开):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* | ----------------------|---------------------- --|-- --|-- -----|------
* 1bit不用 41bit 时间戳 数据标识id 机器id 序列号id
*
* - 1位标识,二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0
* - 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* - 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
* - 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
* - 加起来刚好64位,为一个Long型。
* SnowFlake的优点是
* - 整体上按照时间自增排序
* - 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
* - 并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
export default class SnowFlake {
constructor(_workerId, _dataCenterId, _sequence) {
// 开始时间截 (2020-01-01),这个可以设置开始使用该系统的时间,可往后使用69年
this.twepoch = 1288834974657;
this.workerIdBits = 5;
this.dataCenterIdBits = 5;
this.maxWrokerId = -1 ^ (-1 << this.workerIdBits); // 值为:31
this.maxDataCenterId = -1 ^ (-1 << this.dataCenterIdBits); // 值为:31
this.sequenceBits = 12;
this.workerIdShift = this.sequenceBits; // 值为:12
this.dataCenterIdShift = this.sequenceBits + this.workerIdBits; // 值为:17
this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits; // 值为:22
this.sequenceMask = -1 ^ (-1 << this.sequenceBits); // 值为:4095
this.lastTimestamp = -1;
//设置默认值,从环境变量取
this.workerId = 1;
this.dataCenterId = 1;
this.sequence = 0;
if (this.workerId > this.maxWrokerId || this.workerId < 0) {
throw new Error('config.worker_id must max than 0 and small than maxWrokerId-[' + this.maxWrokerId + ']');
}
if (this.dataCenterId > this.maxDataCenterId || this.dataCenterId < 0) {
throw new Error('config.data_center_id must max than 0 and small than maxDataCenterId-[' + this.maxDataCenterId + ']');
}
this.workerId = _workerId;
this.dataCenterId = _dataCenterId;
this.sequence = _sequence;
}
tilNextMillis(lastTimestamp) {
var timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
timeGen() {
return Date.now();
}
nextId() {
var timestamp = this.timeGen();
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards. Refusing to generate id for ' +
(this.lastTimestamp - timestamp));
}
if (this.lastTimestamp === timestamp) {
this.sequence = (this.sequence + 1) & this.sequenceMask;
if (this.sequence === 0) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
var shiftNum = (this.dataCenterId << this.dataCenterIdShift) |
(this.workerId << this.workerIdShift) |
this.sequence; // dataCenterId:1,workerId:1,sequence:0 shiftNum:135168
var nfirst = new bigInt(String(timestamp - this.twepoch), 10);
nfirst = nfirst.shiftLeft(this.timestampLeftShift);
var nnextId = nfirst.or(new bigInt(String(shiftNum), 10)).toString(10);
return nnextId;
}
/**
* 获取更安全的随机ID(解决连续输出id会出现重复的问题)
* 尽可能的避免重复
* @param {int} repeatRate 重复率默认值100(注释:最小是1,最大值越大,重复的概率越低,不过还需要考虑性能的问题,并不是越大越好,只是相对而言)
*/
guid(repeatRate = 100) {
let arr = []
let ranNum = Math.floor(window.crypto.getRandomValues(new Uint8Array(1)) * 0.001 * repeatRate)
for (let index = 0; index < repeatRate; index++) {
arr[index] = this.nextId()
}
return arr[ranNum]
}
}
let sk = new SnowFlake()
console.log(sk.guid())
NodeJs版本
const bigInt = require('big-integer')
const crypto = require('crypto')
class SnowFlake {
constructor(_workerId, _dataCenterId, _sequence) {
// 开始时间截 (2020-01-01),这个可以设置开始使用该系统的时间,可往后使用69年
this.twepoch = 1288834974657;
this.workerIdBits = 5;
this.dataCenterIdBits = 5;
this.maxWrokerId = -1 ^ (-1 << this.workerIdBits); // 值为:31
this.maxDataCenterId = -1 ^ (-1 << this.dataCenterIdBits); // 值为:31
this.sequenceBits = 12;
this.workerIdShift = this.sequenceBits; // 值为:12
this.dataCenterIdShift = this.sequenceBits + this.workerIdBits; // 值为:17
this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits; // 值为:22
this.sequenceMask = -1 ^ (-1 << this.sequenceBits); // 值为:4095
this.lastTimestamp = -1;
//设置默认值,从环境变量取
this.workerId = 1;
this.dataCenterId = 1;
this.sequence = 0;
if (this.workerId > this.maxWrokerId || this.workerId < 0) {
throw new Error('config.worker_id must max than 0 and small than maxWrokerId-[' + this.maxWrokerId + ']');
}
if (this.dataCenterId > this.maxDataCenterId || this.dataCenterId < 0) {
throw new Error('config.data_center_id must max than 0 and small than maxDataCenterId-[' + this.maxDataCenterId + ']');
}
this.workerId = _workerId;
this.dataCenterId = _dataCenterId;
this.sequence = _sequence;
}
tilNextMillis(lastTimestamp) {
var timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
timeGen() {
return Date.now();
}
nextId() {
var timestamp = this.timeGen();
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards. Refusing to generate id for ' +
(this.lastTimestamp - timestamp));
}
if (this.lastTimestamp === timestamp) {
this.sequence = (this.sequence + 1) & this.sequenceMask;
if (this.sequence === 0) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
var shiftNum = (this.dataCenterId << this.dataCenterIdShift) |
(this.workerId << this.workerIdShift) |
this.sequence; // dataCenterId:1,workerId:1,sequence:0 shiftNum:135168
var nfirst = new bigInt(String(timestamp - this.twepoch), 10);
nfirst = nfirst.shiftLeft(this.timestampLeftShift);
var nnextId = nfirst.or(new bigInt(String(shiftNum), 10)).toString(10);
return nnextId;
}
/**
* 获取更安全的随机ID(解决连续输出id会出现重复的问题)
* 尽可能的避免重复
* @param {int} repeatRate 重复率默认值100(注释:最小是1,最大值越大,重复的概率越低,不过还需要考虑性能的问题,并不是越大越好,只是相对而言)
*/
guid(repeatRate = 100) {
let arr = []
let ranNum = Math.floor(crypto.randomFillSync(new Uint8Array(1)) * 0.001 * repeatRate)
for (let index = 0; index < repeatRate; index++) {
arr[index] = this.nextId()
}
return arr[ranNum]
}
}