xy-server/game/gear/SnowFlake53.ts

141 lines
6.3 KiB
TypeScript
Raw Permalink Normal View History

2025-04-23 09:34:08 +08:00
import moment, { months } from "moment";
import SKLogger from "./SKLogger";
/**
* SnowFlake 53
*
* SnowFlake的结构如下(53bits-):
* 1bit不用 31bit 5bit id 16bit id
*
* - 11id一般都使用整数0
* - 31()31 - id生成器开始使用的时间IdWorker类的startTime属性41使69T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* - 5325workerId
* - 1616()65535ID序号
* - 53Long型
* SnowFlake的优点是
* -
* - ID碰撞(ID和机器ID作区分)
* - SnowFlake每秒能够产生26万ID左右
*/
export default class SnowFlack53 {
// 开始时间截 (2021-01-01 12:00:00),这个可以设置开始使用该系统的时间 1609473600
private readonly twepoch: bigint = 1609473600n;
// 位数划分 [数据标识id(5bit 31)、机器id(5bit 31)](合计共支持1024个节点)、序列id(12bit 4095)
private readonly workerIdBits: bigint = 5n; // 标识Id
private readonly sequenceBits: bigint = 16n; // 序列Id
// 支持的最大十进制id
// 这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数
// -1 左移5位后与 -1 异或
private readonly maxWorkerId: bigint = -1n ^ (-1n << this.workerIdBits);
// 生成序列的掩码这里为65535
private readonly sequenceMask: bigint = -1n ^ (-1n << this.sequenceBits);
// 机器ID向左移16位 数据标识id向左移17位(12+5) 时间截向左移21位(5+16)
private readonly workerIdShift: bigint = this.sequenceBits;
private readonly dataCenterIdShift: bigint = this.sequenceBits + this.workerIdBits;
private readonly timestampLeftShift: bigint = this.dataCenterIdShift;
// 工作机器ID(0~31) 毫秒内序列(0~65535)
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} sequence (0~65535)
*/
constructor(workerId: bigint) {
if (workerId > this.maxWorkerId || workerId < 0n) {
throw new Error(
`机器ID不能大于${this.maxWorkerId}或小于0!`,
)
}
this.workerId = workerId;
return this;
}
/**
* ID (线)
*
* @returns {number} SnowflakeId 53id
*/
public nextId(): number {
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 nextId: BigInt = (
((timestamp - this.twepoch) << this.timestampLeftShift) | // 时间数据左移21
(this.workerId << this.workerIdShift) | // 机器id左移 16
this.sequence
);
let result: number = parseInt(nextId.toString());
if (Number.isSafeInteger(result)) {
return result;
} else {
let info = `分布式ID超出JS整数安全范围`;
SKLogger.warn(info);
throw new Error(info);
}
}
/**
*
* @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} ()
*/
private timeGen(): bigint {
let result = BigInt(Math.floor(new Date().valueOf() / 1000));
return result;
}
// 获得分步式自增ID日期
public getDate(id: bigint): string {
let timestamp = ((id >> this.timestampLeftShift) + this.twepoch) * 1000n;
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;
}
}