NodeJS 实现 Snowflake 算法, TypeScript 实现 Snowflake, 分布式唯一ID,Twitter Snowflake,高并发唯一id,全局唯一id,不重复id

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 };

------(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值