@cdlab996/genid 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/index.cjs +470 -0
- package/dist/index.d.cts +316 -0
- package/dist/index.d.ts +316 -0
- package/dist/index.js +469 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
//#region src/types/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* ID 生成算法类型
|
|
4
|
+
*/
|
|
5
|
+
let GenidMethod = /* @__PURE__ */ function(GenidMethod$1) {
|
|
6
|
+
/** 漂移算法(推荐用于高性能场景) */
|
|
7
|
+
GenidMethod$1[GenidMethod$1["DRIFT"] = 1] = "DRIFT";
|
|
8
|
+
/** 传统算法 */
|
|
9
|
+
GenidMethod$1[GenidMethod$1["TRADITIONAL"] = 2] = "TRADITIONAL";
|
|
10
|
+
return GenidMethod$1;
|
|
11
|
+
}({});
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/index.ts
|
|
15
|
+
/**
|
|
16
|
+
* 优化版 Snowflake ID 生成器
|
|
17
|
+
*
|
|
18
|
+
* 基于 Snowflake 算法的高性能分布式唯一 ID 生成器,支持漂移算法和时钟回拨处理。
|
|
19
|
+
*
|
|
20
|
+
* 特性:
|
|
21
|
+
* - 漂移算法:提升高并发下的性能
|
|
22
|
+
* - 优雅处理时钟回拨,不阻塞生成
|
|
23
|
+
* - 可配置的位长度,灵活性高
|
|
24
|
+
* - 支持传统和漂移两种生成方法
|
|
25
|
+
*
|
|
26
|
+
* ⚠️ 注意:此实例不是线程安全的,每个 Worker/进程应该创建独立的实例并使用不同的 workerId
|
|
27
|
+
*/
|
|
28
|
+
var GenidOptimized = class {
|
|
29
|
+
method;
|
|
30
|
+
baseTime;
|
|
31
|
+
workerId;
|
|
32
|
+
workerIdBitLength;
|
|
33
|
+
seqBitLength;
|
|
34
|
+
maxSeqNumber;
|
|
35
|
+
minSeqNumber;
|
|
36
|
+
topOverCostCount;
|
|
37
|
+
_timestampShift;
|
|
38
|
+
_currentSeqNumber;
|
|
39
|
+
_lastTimeTick;
|
|
40
|
+
_turnBackTimeTick;
|
|
41
|
+
_turnBackIndex;
|
|
42
|
+
_isOverCost;
|
|
43
|
+
_overCostCountInOneTerm;
|
|
44
|
+
_stats;
|
|
45
|
+
/**
|
|
46
|
+
* 构造函数,初始化 ID 生成器
|
|
47
|
+
*
|
|
48
|
+
* @param {Object} options - 配置选项
|
|
49
|
+
* @param {number} options.workerId - 工作节点/机器 ID(必须,范围 0 到 2^workerIdBitLength-1)
|
|
50
|
+
* @param {GenidMethod} [options.method=GenidMethod.DRIFT] - 算法类型
|
|
51
|
+
* @param {number} [options.baseTime=1577836800000] - 起始时间戳(毫秒,默认:2020-01-01)
|
|
52
|
+
* @param {number} [options.workerIdBitLength=6] - 工作节点 ID 的位数(1-15,默认:6)
|
|
53
|
+
* @param {number} [options.seqBitLength=6] - 序列号的位数(3-21,默认:6)
|
|
54
|
+
* @param {number} [options.maxSeqNumber] - 最大序列号(默认:2^seqBitLength-1)
|
|
55
|
+
* @param {number} [options.minSeqNumber=5] - 最小序列号(默认:5,0-4 保留)
|
|
56
|
+
* @param {number} [options.topOverCostCount=2000] - 最大漂移次数(默认:2000)
|
|
57
|
+
*
|
|
58
|
+
* @throws {Error} 如果缺少 workerId 或配置无效
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const genid = new GenidOptimized({ workerId: 1 });
|
|
62
|
+
* const id = genid.nextId();
|
|
63
|
+
*/
|
|
64
|
+
constructor(options) {
|
|
65
|
+
if (options.workerId === void 0 || options.workerId === null) throw new Error("[GenidOptimized] workerId 是必须参数");
|
|
66
|
+
const config = this._initConfig(options);
|
|
67
|
+
this._validateConfig(config);
|
|
68
|
+
this._initVariables(config);
|
|
69
|
+
this._stats = {
|
|
70
|
+
totalGenerated: 0n,
|
|
71
|
+
overCostCount: 0n,
|
|
72
|
+
turnBackCount: 0n,
|
|
73
|
+
startTime: Date.now()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 初始化配置,设置默认值
|
|
78
|
+
* @private
|
|
79
|
+
* @param {Object} options - 用户提供的配置
|
|
80
|
+
* @returns {Object} 合并后的配置对象
|
|
81
|
+
*/
|
|
82
|
+
_initConfig(options) {
|
|
83
|
+
const config = {
|
|
84
|
+
workerId: options.workerId,
|
|
85
|
+
method: options.method === GenidMethod.TRADITIONAL ? GenidMethod.TRADITIONAL : GenidMethod.DRIFT,
|
|
86
|
+
baseTime: options.baseTime && options.baseTime > 0 ? options.baseTime : (/* @__PURE__ */ new Date("2020-01-01")).valueOf(),
|
|
87
|
+
workerIdBitLength: options.workerIdBitLength && options.workerIdBitLength > 0 ? options.workerIdBitLength : 6,
|
|
88
|
+
seqBitLength: options.seqBitLength && options.seqBitLength > 0 ? options.seqBitLength : 6,
|
|
89
|
+
maxSeqNumber: 0,
|
|
90
|
+
minSeqNumber: 0,
|
|
91
|
+
topOverCostCount: 0
|
|
92
|
+
};
|
|
93
|
+
config.maxSeqNumber = options.maxSeqNumber && options.maxSeqNumber > 0 ? options.maxSeqNumber : (1 << config.seqBitLength) - 1;
|
|
94
|
+
config.minSeqNumber = options.minSeqNumber && options.minSeqNumber > 0 ? options.minSeqNumber : 5;
|
|
95
|
+
config.topOverCostCount = options.topOverCostCount && options.topOverCostCount > 0 ? options.topOverCostCount : 2e3;
|
|
96
|
+
return config;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 验证配置参数的有效性
|
|
100
|
+
* @private
|
|
101
|
+
* @param {Object} config - 配置对象
|
|
102
|
+
* @throws {Error} 如果配置无效
|
|
103
|
+
*/
|
|
104
|
+
_validateConfig(config) {
|
|
105
|
+
const { workerId, workerIdBitLength, seqBitLength, minSeqNumber, maxSeqNumber } = config;
|
|
106
|
+
if (workerIdBitLength < 1 || workerIdBitLength > 15) throw new Error("[GenidOptimized] workerIdBitLength 必须在 1 到 15 之间");
|
|
107
|
+
if (seqBitLength < 3 || seqBitLength > 21) throw new Error("[GenidOptimized] seqBitLength 必须在 3 到 21 之间");
|
|
108
|
+
if (workerIdBitLength + seqBitLength > 22) throw new Error("[GenidOptimized] workerIdBitLength + seqBitLength 不能超过 22");
|
|
109
|
+
const maxWorkerId = (1 << workerIdBitLength) - 1;
|
|
110
|
+
if (workerId < 0 || workerId > maxWorkerId) throw new Error(`[GenidOptimized] workerId 必须在 0 到 ${maxWorkerId} 之间`);
|
|
111
|
+
if (minSeqNumber < 5) throw new Error("[GenidOptimized] minSeqNumber 必须至少为 5(0-4 保留)");
|
|
112
|
+
if (maxSeqNumber < minSeqNumber) throw new Error("[GenidOptimized] maxSeqNumber 必须大于或等于 minSeqNumber");
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 初始化实例变量
|
|
116
|
+
* @private
|
|
117
|
+
* @param {Object} config - 配置对象
|
|
118
|
+
*/
|
|
119
|
+
_initVariables(config) {
|
|
120
|
+
this.method = BigInt(config.method);
|
|
121
|
+
this.baseTime = BigInt(config.baseTime);
|
|
122
|
+
this.workerId = BigInt(config.workerId);
|
|
123
|
+
this.workerIdBitLength = BigInt(config.workerIdBitLength);
|
|
124
|
+
this.seqBitLength = BigInt(config.seqBitLength);
|
|
125
|
+
this.maxSeqNumber = BigInt(config.maxSeqNumber);
|
|
126
|
+
this.minSeqNumber = BigInt(config.minSeqNumber);
|
|
127
|
+
this.topOverCostCount = BigInt(config.topOverCostCount);
|
|
128
|
+
this._timestampShift = this.workerIdBitLength + this.seqBitLength;
|
|
129
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
130
|
+
this._lastTimeTick = 0n;
|
|
131
|
+
this._turnBackTimeTick = 0n;
|
|
132
|
+
this._turnBackIndex = 0;
|
|
133
|
+
this._isOverCost = false;
|
|
134
|
+
this._overCostCountInOneTerm = 0n;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 获取当前时间戳(相对于 baseTime 的毫秒数)
|
|
138
|
+
* @private
|
|
139
|
+
* @returns {bigint} 当前时间戳
|
|
140
|
+
*/
|
|
141
|
+
_getCurrentTimeTick() {
|
|
142
|
+
return BigInt(Date.now()) - this.baseTime;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 等待下一毫秒
|
|
146
|
+
* @private
|
|
147
|
+
* @returns {bigint} 下一毫秒时间戳
|
|
148
|
+
*/
|
|
149
|
+
_getNextTimeTick() {
|
|
150
|
+
let timeTick = this._getCurrentTimeTick();
|
|
151
|
+
let spinCount = 0;
|
|
152
|
+
const maxSpinCount = 1e6;
|
|
153
|
+
while (timeTick <= this._lastTimeTick) {
|
|
154
|
+
spinCount++;
|
|
155
|
+
if (spinCount > maxSpinCount)
|
|
156
|
+
/**
|
|
157
|
+
* 如果自旋太多次,强制返回当前时间 + 1
|
|
158
|
+
* 这种情况理论上不应该发生,除非系统时间出现严重问题
|
|
159
|
+
*/
|
|
160
|
+
return this._lastTimeTick + 1n;
|
|
161
|
+
timeTick = this._getCurrentTimeTick();
|
|
162
|
+
}
|
|
163
|
+
return timeTick;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 根据组件计算 ID
|
|
167
|
+
* @private
|
|
168
|
+
* @param {bigint} useTimeTick - 使用的时间戳
|
|
169
|
+
* @returns {bigint} 计算得到的 ID
|
|
170
|
+
*/
|
|
171
|
+
_calcId(useTimeTick) {
|
|
172
|
+
const result = (BigInt(useTimeTick) << this._timestampShift) + (this.workerId << this.seqBitLength) + this._currentSeqNumber;
|
|
173
|
+
this._currentSeqNumber++;
|
|
174
|
+
this._stats.totalGenerated++;
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 计算时钟回拨时的 ID
|
|
179
|
+
* @private
|
|
180
|
+
* @param {bigint} useTimeTick - 使用的时间戳
|
|
181
|
+
* @returns {bigint} 计算得到的 ID
|
|
182
|
+
*/
|
|
183
|
+
_calcTurnBackId(useTimeTick) {
|
|
184
|
+
const result = (BigInt(useTimeTick) << this._timestampShift) + (this.workerId << this.seqBitLength) + BigInt(this._turnBackIndex);
|
|
185
|
+
this._turnBackTimeTick--;
|
|
186
|
+
this._stats.totalGenerated++;
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 处理漂移情况(漂移算法)
|
|
191
|
+
* @private
|
|
192
|
+
* @returns {bigint} 生成的 ID
|
|
193
|
+
*/
|
|
194
|
+
_nextOverCostId() {
|
|
195
|
+
const currentTimeTick = this._getCurrentTimeTick();
|
|
196
|
+
if (currentTimeTick > this._lastTimeTick) {
|
|
197
|
+
this._endOverCostAction(currentTimeTick);
|
|
198
|
+
if (this._currentSeqNumber <= this.maxSeqNumber) {
|
|
199
|
+
const result = this._calcId(this._lastTimeTick);
|
|
200
|
+
this._lastTimeTick = currentTimeTick;
|
|
201
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
202
|
+
this._isOverCost = false;
|
|
203
|
+
this._overCostCountInOneTerm = 0n;
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
this._lastTimeTick = currentTimeTick;
|
|
207
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
208
|
+
this._isOverCost = false;
|
|
209
|
+
this._overCostCountInOneTerm = 0n;
|
|
210
|
+
return this._calcId(this._lastTimeTick);
|
|
211
|
+
}
|
|
212
|
+
if (this._overCostCountInOneTerm >= this.topOverCostCount) {
|
|
213
|
+
this._endOverCostAction(currentTimeTick);
|
|
214
|
+
this._lastTimeTick = this._getNextTimeTick();
|
|
215
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
216
|
+
this._isOverCost = false;
|
|
217
|
+
this._overCostCountInOneTerm = 0n;
|
|
218
|
+
return this._calcId(this._lastTimeTick);
|
|
219
|
+
}
|
|
220
|
+
if (this._currentSeqNumber > this.maxSeqNumber) {
|
|
221
|
+
this._lastTimeTick++;
|
|
222
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
223
|
+
this._isOverCost = true;
|
|
224
|
+
this._overCostCountInOneTerm++;
|
|
225
|
+
return this._calcId(this._lastTimeTick);
|
|
226
|
+
}
|
|
227
|
+
return this._calcId(this._lastTimeTick);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* 正常生成 ID
|
|
231
|
+
* @private
|
|
232
|
+
* @returns {bigint} 生成的 ID
|
|
233
|
+
*/
|
|
234
|
+
_nextNormalId() {
|
|
235
|
+
const currentTimeTick = this._getCurrentTimeTick();
|
|
236
|
+
if (currentTimeTick < this._lastTimeTick) {
|
|
237
|
+
if (this._turnBackTimeTick < 1) {
|
|
238
|
+
this._turnBackTimeTick = this._lastTimeTick - 1n;
|
|
239
|
+
this._turnBackIndex++;
|
|
240
|
+
if (this._turnBackIndex > 4) {
|
|
241
|
+
this._lastTimeTick = this._getNextTimeTick();
|
|
242
|
+
this._turnBackIndex = 0;
|
|
243
|
+
this._turnBackTimeTick = 0n;
|
|
244
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
245
|
+
return this._calcId(this._lastTimeTick);
|
|
246
|
+
}
|
|
247
|
+
this._beginTurnBackAction(this._turnBackTimeTick);
|
|
248
|
+
this._stats.turnBackCount++;
|
|
249
|
+
}
|
|
250
|
+
return this._calcTurnBackId(this._turnBackTimeTick);
|
|
251
|
+
}
|
|
252
|
+
if (this._turnBackTimeTick > 0) {
|
|
253
|
+
this._endTurnBackAction(this._turnBackTimeTick);
|
|
254
|
+
this._turnBackTimeTick = 0n;
|
|
255
|
+
this._turnBackIndex = 0;
|
|
256
|
+
}
|
|
257
|
+
if (currentTimeTick > this._lastTimeTick) {
|
|
258
|
+
this._lastTimeTick = currentTimeTick;
|
|
259
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
260
|
+
return this._calcId(this._lastTimeTick);
|
|
261
|
+
}
|
|
262
|
+
if (this._currentSeqNumber > this.maxSeqNumber) {
|
|
263
|
+
this._beginOverCostAction(currentTimeTick);
|
|
264
|
+
this._lastTimeTick++;
|
|
265
|
+
this._currentSeqNumber = this.minSeqNumber;
|
|
266
|
+
this._isOverCost = true;
|
|
267
|
+
this._overCostCountInOneTerm = 1n;
|
|
268
|
+
this._stats.overCostCount++;
|
|
269
|
+
return this._calcId(this._lastTimeTick);
|
|
270
|
+
}
|
|
271
|
+
return this._calcId(this._lastTimeTick);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 钩子函数:开始漂移操作(可被子类重写)
|
|
275
|
+
* @protected
|
|
276
|
+
* @param {bigint} useTimeTick - 当前时间戳
|
|
277
|
+
*/
|
|
278
|
+
_beginOverCostAction(useTimeTick) {}
|
|
279
|
+
/**
|
|
280
|
+
* 钩子函数:结束漂移操作(可被子类重写)
|
|
281
|
+
* @protected
|
|
282
|
+
* @param {bigint} useTimeTick - 当前时间戳
|
|
283
|
+
*/
|
|
284
|
+
_endOverCostAction(useTimeTick) {}
|
|
285
|
+
/**
|
|
286
|
+
* 钩子函数:开始时钟回拨操作(可被子类重写)
|
|
287
|
+
* @protected
|
|
288
|
+
* @param {bigint} useTimeTick - 当前时间戳
|
|
289
|
+
*/
|
|
290
|
+
_beginTurnBackAction(useTimeTick) {}
|
|
291
|
+
/**
|
|
292
|
+
* 钩子函数:结束时钟回拨操作(可被子类重写)
|
|
293
|
+
* @protected
|
|
294
|
+
* @param {bigint} useTimeTick - 当前时间戳
|
|
295
|
+
*/
|
|
296
|
+
_endTurnBackAction(useTimeTick) {}
|
|
297
|
+
/**
|
|
298
|
+
* 生成下一个 ID
|
|
299
|
+
*
|
|
300
|
+
* @returns {number} 唯一 ID(Number 类型)
|
|
301
|
+
* @throws {Error} 如果 ID 超出 JavaScript 安全整数范围
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* const id = genid.nextNumber();
|
|
305
|
+
* console.log(id); // 123456789012345
|
|
306
|
+
*/
|
|
307
|
+
nextNumber() {
|
|
308
|
+
const id = this._isOverCost ? this._nextOverCostId() : this._nextNormalId();
|
|
309
|
+
if (id >= 9007199254740992n) throw new Error(`[GenidOptimized] 生成的 ID ${id.toString()} 超出 JavaScript 安全整数范围 (9007199254740992)。请使用 nextBigId() 方法。`);
|
|
310
|
+
return Number(id);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* 生成下一个 ID
|
|
314
|
+
*
|
|
315
|
+
* 如果 ID 在安全范围内返回 Number,否则返回 BigInt
|
|
316
|
+
*
|
|
317
|
+
* @returns {number|bigint} 唯一 ID
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* const id = genid.nextId();
|
|
321
|
+
* console.log(typeof id); // 'number' 或 'bigint'
|
|
322
|
+
*/
|
|
323
|
+
nextId() {
|
|
324
|
+
const id = this._isOverCost ? this._nextOverCostId() : this._nextNormalId();
|
|
325
|
+
return id >= 9007199254740992n ? id : Number(id);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 生成下一个 ID
|
|
329
|
+
*
|
|
330
|
+
* @returns {bigint} 唯一 ID(BigInt 类型)
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* const id = genid.nextBigId();
|
|
334
|
+
* console.log(id); // 123456789012345678n
|
|
335
|
+
*/
|
|
336
|
+
nextBigId() {
|
|
337
|
+
return this._isOverCost ? this._nextOverCostId() : this._nextNormalId();
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 批量生成 ID
|
|
341
|
+
*
|
|
342
|
+
* @param {number} count - 要生成的 ID 数量
|
|
343
|
+
* @param {boolean} [asBigInt=false] - 是否返回 BigInt 数组
|
|
344
|
+
* @returns {Array<number|bigint>} 唯一 ID 数组
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* const ids = genid.nextBatch(100);
|
|
348
|
+
* const bigIds = genid.nextBatch(100, true);
|
|
349
|
+
*/
|
|
350
|
+
nextBatch(count, asBigInt = false) {
|
|
351
|
+
if (count <= 0) throw new Error("[GenidOptimized] 批量生成数量必须大于 0");
|
|
352
|
+
const ids = [];
|
|
353
|
+
for (let i = 0; i < count; i++) ids.push(asBigInt ? this.nextBigId() : this.nextId());
|
|
354
|
+
return ids;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 解析 ID,提取其组成部分
|
|
358
|
+
*
|
|
359
|
+
* @param {number|bigint|string} id - 要解析的 ID
|
|
360
|
+
* @returns {Object} 解析结果
|
|
361
|
+
* @returns {Date} return.timestamp - 生成时间戳
|
|
362
|
+
* @returns {number} return.timestampMs - 时间戳(毫秒)
|
|
363
|
+
* @returns {number} return.workerId - 工作节点 ID
|
|
364
|
+
* @returns {number} return.sequence - 序列号
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* const info = genid.parse(id);
|
|
368
|
+
* console.log(info);
|
|
369
|
+
* // {
|
|
370
|
+
* // timestamp: Date,
|
|
371
|
+
* // timestampMs: 1609459200000,
|
|
372
|
+
* // workerId: 1,
|
|
373
|
+
* // sequence: 42
|
|
374
|
+
* // }
|
|
375
|
+
*/
|
|
376
|
+
parse(id) {
|
|
377
|
+
const idBigInt = BigInt(id);
|
|
378
|
+
if (idBigInt < 0n) throw new Error("[GenidOptimized] ID 不能为负数");
|
|
379
|
+
const timestamp = (idBigInt >> this._timestampShift) + this.baseTime;
|
|
380
|
+
const workerIdMask = (1n << this.workerIdBitLength) - 1n;
|
|
381
|
+
const workerId = idBigInt >> this.seqBitLength & workerIdMask;
|
|
382
|
+
const sequence = idBigInt & (1n << this.seqBitLength) - 1n;
|
|
383
|
+
return {
|
|
384
|
+
timestamp: new Date(Number(timestamp)),
|
|
385
|
+
timestampMs: Number(timestamp),
|
|
386
|
+
workerId: Number(workerId),
|
|
387
|
+
sequence: Number(sequence)
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* 获取生成器统计信息
|
|
392
|
+
*
|
|
393
|
+
* @returns {Object} 统计数据
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* const stats = genid.getStats();
|
|
397
|
+
* console.log(stats);
|
|
398
|
+
*/
|
|
399
|
+
getStats() {
|
|
400
|
+
const uptime = Date.now() - this._stats.startTime;
|
|
401
|
+
const totalGenerated = Number(this._stats.totalGenerated);
|
|
402
|
+
return {
|
|
403
|
+
totalGenerated,
|
|
404
|
+
overCostCount: Number(this._stats.overCostCount),
|
|
405
|
+
turnBackCount: Number(this._stats.turnBackCount),
|
|
406
|
+
uptimeMs: uptime,
|
|
407
|
+
avgPerSecond: uptime > 0 ? Math.floor(totalGenerated / uptime * 1e3) : 0,
|
|
408
|
+
currentState: this._isOverCost ? "OVER_COST" : "NORMAL"
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 重置统计数据
|
|
413
|
+
*/
|
|
414
|
+
resetStats() {
|
|
415
|
+
this._stats = {
|
|
416
|
+
totalGenerated: 0n,
|
|
417
|
+
overCostCount: 0n,
|
|
418
|
+
turnBackCount: 0n,
|
|
419
|
+
startTime: Date.now()
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* 获取配置信息
|
|
424
|
+
*
|
|
425
|
+
* @returns {Object} 配置详情
|
|
426
|
+
*/
|
|
427
|
+
getConfig() {
|
|
428
|
+
const maxWorkerId = (1 << Number(this.workerIdBitLength)) - 1;
|
|
429
|
+
const maxSequence = (1 << Number(this.seqBitLength)) - 1;
|
|
430
|
+
const idsPerMs = Number(this.maxSeqNumber - this.minSeqNumber + 1n);
|
|
431
|
+
return {
|
|
432
|
+
method: Number(this.method) === GenidMethod.DRIFT ? "DRIFT" : "TRADITIONAL",
|
|
433
|
+
workerId: Number(this.workerId),
|
|
434
|
+
workerIdRange: `0-${maxWorkerId}`,
|
|
435
|
+
sequenceRange: `${Number(this.minSeqNumber)}-${Number(this.maxSeqNumber)}`,
|
|
436
|
+
maxSequence,
|
|
437
|
+
idsPerMillisecond: idsPerMs,
|
|
438
|
+
baseTime: new Date(Number(this.baseTime)),
|
|
439
|
+
timestampBits: 64 - Number(this.workerIdBitLength + this.seqBitLength),
|
|
440
|
+
workerIdBits: Number(this.workerIdBitLength),
|
|
441
|
+
sequenceBits: Number(this.seqBitLength)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* 将 ID 格式化为二进制字符串以便调试
|
|
446
|
+
*
|
|
447
|
+
* @param {number|bigint|string} id - 要格式化的 ID
|
|
448
|
+
* @returns {string} 格式化的二进制表示
|
|
449
|
+
*/
|
|
450
|
+
formatBinary(id) {
|
|
451
|
+
const idBigInt = BigInt(id);
|
|
452
|
+
if (idBigInt < 0n) throw new Error("[GenidOptimized] ID 不能为负数");
|
|
453
|
+
const binary = idBigInt.toString(2).padStart(64, "0");
|
|
454
|
+
const parsed = this.parse(id);
|
|
455
|
+
const timestampBits = 64 - Number(this.workerIdBitLength + this.seqBitLength);
|
|
456
|
+
const workerIdStart = timestampBits;
|
|
457
|
+
const seqStart = workerIdStart + Number(this.workerIdBitLength);
|
|
458
|
+
return [
|
|
459
|
+
`ID: ${id}`,
|
|
460
|
+
"Binary (64-bit):",
|
|
461
|
+
`${binary.slice(0, timestampBits)} - 时间戳 (${timestampBits} bits) = ${parsed.timestamp.toISOString()}`,
|
|
462
|
+
`${binary.slice(timestampBits, workerIdStart + Number(this.workerIdBitLength))} - 工作节点 ID (${this.workerIdBitLength} bits) = ${parsed.workerId}`,
|
|
463
|
+
`${binary.slice(seqStart)} - 序列号 (${this.seqBitLength} bits) = ${parsed.sequence}`
|
|
464
|
+
].join("\n");
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
//#endregion
|
|
469
|
+
export { GenidOptimized };
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cdlab996/genid",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "基于 Snowflake 算法的高性能分布式唯一 ID 生成器",
|
|
6
|
+
"author": "wudi <wuchendi96@gmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/WuChenDi",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/WuChenDi/genid.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/WuChenDi/genid/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"snowflake",
|
|
18
|
+
"id-generator",
|
|
19
|
+
"distributed-id",
|
|
20
|
+
"unique-id",
|
|
21
|
+
"genid",
|
|
22
|
+
"uuid",
|
|
23
|
+
"uid",
|
|
24
|
+
"typescript"
|
|
25
|
+
],
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/index.d.cts",
|
|
30
|
+
"default": "./dist/index.cjs"
|
|
31
|
+
},
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"main": "./dist/index.js",
|
|
39
|
+
"module": "./dist/index.js",
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"README.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=20"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsdown",
|
|
51
|
+
"dev": "tsdown --watch",
|
|
52
|
+
"test": "vitest --run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"typecheck": "tsc --project tsconfig.json"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22",
|
|
58
|
+
"tsdown": "^0.15.7",
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
|
+
"vitest": "^3.2.4"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public",
|
|
64
|
+
"registry": "https://registry.npmjs.org/"
|
|
65
|
+
}
|
|
66
|
+
}
|