Twitter Snowflake 算法 NodeJS 下 TypeScript 的实现。
如果还没了解Twitter-Snowflake算法的,麻烦自行百度下!
下面是一张相关图:(图片来自网络,出处不清楚)
如果发现有ID重复,大概率是用法不对,请看代码注释部分。
import moment from 'moment-timezone';
import AsyncLock from 'async-lock';
/**
* 常规版本
* 重要说明:实际使用,应该单独部署,当作“ID生成器”,以接口方式对外提供生成的ID
* 字节分配:1bit符号标识 + 41bit时间戳 + 10bit工作节点 + 12bit序列号
*/
class StandardSnowflake {
private workerId: bigint;
private sequence: bigint;
private lastTimestamp: bigint;
constructor(workerId: number, sequence: number = 0) {
this.workerId = (BigInt(workerId) & BigInt(0x3ff)) << BigInt(12);
this.sequence = BigInt(sequence);
this.lastTimestamp = BigInt(-1);
}
public async nextId(): Promise<bigint> {
return lock.acquire(`standard-snowflake`, async (done) => {
let result: bigint;
try {
result = this._nextId();
} finally {
done(null, BigInt(result));
}
});
}
private _nextId(): bigint {
let timestamp = BigInt(moment().tz('Asia/Shanghai').valueOf());
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards. Refusing to generate id');
}
if (this.lastTimestamp == timestamp) {
this.sequence = BigInt(this.sequence + BigInt(1)) & BigInt(0xfff);
if (this.sequence == BigInt(0)) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = BigInt(0);
}
this.lastTimestamp = timestamp;
const twepoch = BigInt(1672502400000); // 2023-01-01 00:00:00
const bTimestamp: bigint = BigInt(timestamp - twepoch) << BigInt(22);
return bTimestamp | this.workerId | this.sequence;
}
private tilNextMillis(lastTimestamp: bigint): bigint {
let timestamp = BigInt(moment().tz('Asia/Shanghai').valueOf());
while (timestamp <= lastTimestamp) {
timestamp = BigInt(moment().tz('Asia/Shanghai').valueOf());
}
return timestamp;
}
}
export { StandardSnowflake };
下面是一个简单使用版:
import AsyncLock from 'async-lock';
import moment from 'moment-timezone';
/**
* 简约版本(方便单实例的项目直接用,生成的ID长度看起来较短)
* 重要说明:该代码已经去掉了“机器标识”字段,如果项目是单个实例部署运行,可在项目内直接引用
* 字节分配:1bit符号标识 + 57bit时间戳 + 6bit序列号
*/
class MiniSnowflake {
private sequence: bigint;
private lastTimestamp: bigint;
constructor(sequence: number = 0) {
this.sequence = BigInt(sequence);
this.lastTimestamp = BigInt(-1);
}
public async nextId(): Promise<bigint> {
return lock.acquire(`mini-snowflake`, async (done) => {
let result: bigint;
try {
result = this._nextId();
} finally {
done(null, BigInt(result));
}
});
}
private _nextId(): bigint {
let timestamp = BigInt(moment().tz('Asia/Shanghai').valueOf());
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards. Refusing to generate id');
}
if (this.lastTimestamp == timestamp) {
this.sequence = BigInt(this.sequence + BigInt(1)) & BigInt(0x3f);
if (this.sequence == BigInt(0)) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = BigInt(0);
}
this.lastTimestamp = timestamp;
const twepoch = BigInt(1672502400000); // 2023-01-01 00:00:00
const bTimestamp: bigint = BigInt(timestamp - twepoch) << BigInt(6);
return bTimestamp | this.sequence;
}
private tilNextMillis(lastTimestamp: bigint): bigint {
let timestamp = BigInt(moment().tz('Asia/Shanghai').valueOf());
while (timestamp <= lastTimestamp) {
timestamp = BigInt(moment().tz('Asia/Shanghai').valueOf());
}
return timestamp;
}
}
const miniSnowflake = new MiniSnowflake();
export { miniSnowflake };
------(完)
因篇幅问题不能全部显示,请点此查看更多更全内容