xy-server/game/gear/SnowFlake.ts

145 lines
6.8 KiB
TypeScript
Raw Permalink Normal View History

2025-04-23 09:34:08 +08:00
import moment from "moment";
/**
* Twitter_Snowflake
*
* SnowFlake的结构如下(64bits-):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* | ----------------------|---------------------- --|-- --|-- -----|------
* 1bit不用 41bit id id id
*
* - 11id一般都使用整数0
* - 41()41 - id生成器开始使用的时间IdWorker类的startTime属性41使69T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* - 1010245datacenterId和5位workerId
* - 1212()4096ID序号
* - 64Long型
* SnowFlake的优点是
* -
* - ID碰撞(ID和机器ID作区分)
* - SnowFlake每秒能够产生26万ID左右
*/
export default class SnowFlack {
// 开始时间截 (2021-01-01 12:00:00)这个可以设置开始使用该系统的时间可往后使用69年
private readonly twepoch: bigint = 1609473600000n;
// 位数划分 [数据标识id(5bit 31)、机器id(5bit 31)](合计共支持1024个节点)、序列id(12bit 4095)
private readonly workerIdBits: bigint = 5n; // 标识Id
private readonly dataCenterIdBits: bigint = 5n; // 机器Id
private readonly sequenceBits: bigint = 12n; // 序列Id
// 支持的最大十进制id
// 这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数
// -1 左移5位后与 -1 异或
private readonly maxWorkerId: bigint = -1n ^ (-1n << this.workerIdBits);
private readonly maxDataCenterId: bigint = -1n ^ (-1n << this.dataCenterIdBits);
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private readonly sequenceMask: bigint = -1n ^ (-1n << this.sequenceBits);
// 机器ID向左移12位 数据标识id向左移17位(12+5) 时间截向左移22位(5+5+12)
private readonly workerIdShift: bigint = this.sequenceBits;
private readonly dataCenterIdShift: bigint = this.sequenceBits + this.workerIdBits;
private readonly timestampLeftShift: bigint = this.dataCenterIdShift + this.dataCenterIdBits;
// 工作机器ID(0~31) 数据中心ID(0~31) 毫秒内序列(0~4095)
private sequence: bigint = 0n;
// 上次生成ID的时间截这个是在内存中系统时钟回退+重启后呢)
private lastTimestamp: bigint = -1n;
private readonly workerId: bigint;
private readonly dataCenterId: any;
/**
*
*
* @param {bigint} workerId ID (0~31)
* @param {bigint} dataCenterId ID (0~31)
* @param {bigint} sequence (0~4095)
*/
constructor(workerId: bigint, dataCenterId: bigint) {
if (workerId > this.maxWorkerId || workerId < 0n) {
throw new Error(
`workerId can't be greater than ${this.maxWorkerId} or less than 0`,
)
}
if (dataCenterId > this.maxDataCenterId || dataCenterId < 0n) {
throw new Error(
`dataCenterId can't be greater than ${this.maxDataCenterId} or less than 0`,
)
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
return this;
}
/**
* ID (线)
*
* @returns {bigint} SnowflakeId id
*/
public nextId(): bigint {
let timestamp = this.timeGen();
// 如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
const diff = timestamp - this.lastTimestamp;
if (diff < 0n) {
throw new Error(
`Clock moved backwards. Refusing to generate id for ${-diff} milliseconds`
);
}
// 如果是同一时间生成的,则进行毫秒内序列
if (diff === 0n) {
this.sequence = (this.sequence + 1n) & this.sequenceMask;
// 毫秒内序列溢出
if (this.sequence === 0n) {
// 阻塞到下一个毫秒,获得新的时间戳
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
// 时间戳改变,毫秒内序列重置
this.sequence = 0n;
}
// 保存上次生成ID的时间截
this.lastTimestamp = timestamp;
// 移位并通过或运算拼到一起组成64位的ID
// 将各 bits 位数据移位后或运算合成一个大的64位二进制数据
let result = (
((timestamp - this.twepoch) << this.timestampLeftShift) | // 时间数据左移22
(this.dataCenterId << this.dataCenterIdShift) | // 数据标识id左移 17
(this.workerId << this.workerIdShift) | // 机器id左移 12
this.sequence
);
return result;
}
/**
*
* @param {bigint} lastTimestamp ID的时间截
* @return {bigint}
*/
private tilNextMillis(lastTimestamp: bigint): bigint {
let timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
/**
*
* @return {bigint} ()
*/
timeGen(): bigint {
return BigInt(new Date().valueOf());
}
// 获得分步式自增ID日期
public getDate(id: bigint): string {
let timestamp = ((id >> this.timestampLeftShift) + this.twepoch);
let temp = parseInt(timestamp.toString())
let result = moment(temp).format("YYYY-MM-DD hh:mm:ss");
return result;
}
// 获得分步式自增ID机器ID
public getWorkerId(id: bigint): number {
let workerId = (id >> this.sequenceBits) & this.maxWorkerId;
let result = parseInt(workerId.toString());
return result;
}
// 获得分步式自增ID序列号ID
public getSequence(id: bigint): number {
let sequence = id & this.sequenceMask;
let result = parseInt(sequence.toString());
return result;
}
}