简介:针对JavaScript数字表示限制,改造Twitter的雪花算法SnowFlake,实现一个兼容JS截短位数的53bit分布式ID生成器,确保在JS环境中生成的ID不会溢出,并保持全局唯一性。
在分布式系统中,生成全局唯一的ID是一个常见的需求。Twitter的雪花算法(SnowFlake)是一个广受欢迎的解决方案,它通过组合时间戳、机器标识和序列号来生成64bit的ID。然而,在JavaScript环境中,由于数字的最大安全整数限制(Number.MAX_SAFE_INTEGER,即2^53-1),64bit的ID可能会导致溢出问题。因此,我们需要对SnowFlake算法进行改造,使其兼容JS的53bit表示限制。
首先,我们需要了解SnowFlake算法的基本结构。一个64bit的SnowFlake ID通常包含以下几个部分:
为了兼容JS的53bit限制,我们需要减少ID的位数。一种简单的方法是将时间戳部分从41bit减少到38bit,这样整个ID就变为53bit,完全可以在JS中安全表示。但是,这样做会限制ID的时间范围,因此需要谨慎考虑。
改造后的算法结构如下:
下面是一个简单的JavaScript实现示例:
```javascript
class SnowFlake {
constructor(datacenterId, workerId) {
if (datacenterId > 31 || datacenterId < 0) {
throw new Error(‘Datacenter ID must be between 0 and 31’);
}
if (workerId > 31 || workerId < 0) {
throw new Error(‘Worker ID must be between 0 and 31’);
}
this.twepoch = 1288834974657n; // 起始时间戳(毫秒级),可根据需求调整
this.datacenterIdBits = 5n;
this.workerIdBits = 5n;
this.maxDatacenterId = -1n ^ (-1n << this.datacenterIdBits);
this.maxWorkerId = -1n ^ (-1n << this.workerIdBits);
this.sequenceBits = 4n;
this.sequenceMask = -1n ^ (-1n << this.sequenceBits);
this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.datacenterIdBits;
this.datacenterIdShift = this.sequenceBits + this.workerIdBits;
this.workerIdShift = this.sequenceBits;
this.timestamp = 0n;
this.datacenterId = datacenterId;
this.workerId = workerId;
this.sequence = 0n;
this.lastTimestamp = -1n;
}
_timeGen(): bigint {
return BigInt(Date.now());
}
_tillNextMillis(lastTimestamp: bigint): bigint {
let timestamp = this._timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this._timeGen();
}
return timestamp;
}
_nextId(): bigint {
let timestamp = this._timeGen();
if (this.lastTimestamp === timestamp) {this.sequence = (this.sequence + 1n) & this.sequenceMask;if (this.sequence === 0n) {timestamp = this._tillNextMillis(this.lastTimestamp);}} else {this.sequence = 0n;}if (timestamp < this.lastTimestamp) {throw new Error('Clock moved backwards');}this.lastTimestamp = timestamp;return ((timestamp - this.twepoch) << this.timestampLeftShift) |(this.