* 基本可运行,待优化 * 增加注释 * 增加注释说明,更容易理解 * 提交package-lock.json文件,保证依赖版本一致tags/v1.2
@@ -5,27 +5,27 @@ | |||||
* 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 | * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 | ||||
* 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 | * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 | ||||
* | * | ||||
*/ | |||||
using System; | |||||
using System.Threading; | |||||
*/ | |||||
using System; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Yitter.IdGenerator | |||||
{ | |||||
/// <summary> | |||||
/// 雪花漂移算法 | |||||
/// </summary> | |||||
internal class SnowWorkerM1 : ISnowWorker | |||||
namespace Yitter.IdGenerator | |||||
{ | |||||
/// <summary> | |||||
/// 雪花漂移算法 | |||||
/// </summary> | |||||
internal class SnowWorkerM1 : ISnowWorker | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 基础时间 | /// 基础时间 | ||||
/// </summary> | /// </summary> | ||||
protected readonly DateTime BaseTime; | |||||
protected readonly DateTime BaseTime; | |||||
/// <summary> | |||||
/// 机器码 | |||||
/// </summary> | |||||
/// <summary> | |||||
/// 机器码 | |||||
/// </summary> | |||||
protected readonly ushort WorkerId = 0; | protected readonly ushort WorkerId = 0; | ||||
/// <summary> | /// <summary> | ||||
@@ -40,7 +40,7 @@ namespace Yitter.IdGenerator | |||||
/// <summary> | /// <summary> | ||||
/// 最大序列数(含) | /// 最大序列数(含) | ||||
/// </summary> | |||||
/// </summary> | |||||
protected readonly int MaxSeqNumber = 0; | protected readonly int MaxSeqNumber = 0; | ||||
/// <summary> | /// <summary> | ||||
@@ -57,11 +57,11 @@ namespace Yitter.IdGenerator | |||||
protected static object _SyncLock = new object(); | protected static object _SyncLock = new object(); | ||||
protected ushort _CurrentSeqNumber; | protected ushort _CurrentSeqNumber; | ||||
protected long _LastTimeTick = 0; // -1L | |||||
protected long _TurnBackTimeTick = 0; // -1L; | |||||
protected byte _TurnBackIndex = 0; | |||||
protected bool _IsOverCost = false; | |||||
protected long _LastTimeTick = 0; // -1L | |||||
protected long _TurnBackTimeTick = 0; // -1L; | |||||
protected byte _TurnBackIndex = 0; | |||||
protected bool _IsOverCost = false; | |||||
protected int _OverCostCountInOneTerm = 0; | protected int _OverCostCountInOneTerm = 0; | ||||
protected int _GenCountInOneTerm = 0; | protected int _GenCountInOneTerm = 0; | ||||
protected int _TermIndex = 0; | protected int _TermIndex = 0; | ||||
@@ -71,8 +71,8 @@ namespace Yitter.IdGenerator | |||||
public Action<OverCostActionArg> GenAction { get; set; } | public Action<OverCostActionArg> GenAction { get; set; } | ||||
public SnowWorkerM1(IdGeneratorOptions options) | |||||
public SnowWorkerM1(IdGeneratorOptions options) | |||||
{ | { | ||||
// 1.BaseTime | // 1.BaseTime | ||||
if (options.BaseTime != DateTime.MinValue) | if (options.BaseTime != DateTime.MinValue) | ||||
@@ -117,17 +117,17 @@ namespace Yitter.IdGenerator | |||||
MinSeqNumber = options.MinSeqNumber; | MinSeqNumber = options.MinSeqNumber; | ||||
// 7.Others | // 7.Others | ||||
TopOverCostCount = options.TopOverCostCount; | |||||
TopOverCostCount = options.TopOverCostCount; | |||||
if (TopOverCostCount == 0) | if (TopOverCostCount == 0) | ||||
{ | { | ||||
TopOverCostCount = 2000; | TopOverCostCount = 2000; | ||||
} | } | ||||
_TimestampShift = (byte)(WorkerIdBitLength + SeqBitLength); | |||||
_CurrentSeqNumber = options.MinSeqNumber; | |||||
//_BaseTimeTick = BaseTime.Ticks; | |||||
//_StartTimeTick = (long)(DateTime.UtcNow.Subtract(BaseTime).TotalMilliseconds) - Environment.TickCount; | |||||
_TimestampShift = (byte)(WorkerIdBitLength + SeqBitLength); | |||||
_CurrentSeqNumber = options.MinSeqNumber; | |||||
//_BaseTimeTick = BaseTime.Ticks; | |||||
//_StartTimeTick = (long)(DateTime.UtcNow.Subtract(BaseTime).TotalMilliseconds) - Environment.TickCount; | |||||
} | } | ||||
@@ -137,7 +137,7 @@ namespace Yitter.IdGenerator | |||||
{ | { | ||||
GenAction(arg); | GenAction(arg); | ||||
}); | }); | ||||
} | |||||
} | |||||
private void BeginOverCostAction(in long useTimeTick) | private void BeginOverCostAction(in long useTimeTick) | ||||
{ | { | ||||
@@ -338,11 +338,11 @@ namespace Yitter.IdGenerator | |||||
return result; | return result; | ||||
} | } | ||||
protected virtual long GetCurrentTimeTick() | |||||
{ | |||||
//return (long)(DateTime.UtcNow - BaseTime).Ticks; | |||||
//return (long)(_StartTimeTick + Environment.TickCount); | |||||
return (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds; | |||||
protected virtual long GetCurrentTimeTick() | |||||
{ | |||||
//return (long)(DateTime.UtcNow - BaseTime).Ticks; | |||||
//return (long)(_StartTimeTick + Environment.TickCount); | |||||
return (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds; | |||||
} | } | ||||
protected virtual long GetNextTimeTick() | protected virtual long GetNextTimeTick() | ||||
@@ -364,6 +364,6 @@ namespace Yitter.IdGenerator | |||||
{ | { | ||||
return _IsOverCost ? NextOverCostId() : NextNormalId(); | return _IsOverCost ? NextOverCostId() : NextNormalId(); | ||||
} | } | ||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -6,6 +6,12 @@ | |||||
代码贡献者:bubao 布宝 | 代码贡献者:bubao 布宝 | ||||
执行测试代码 | |||||
```bash | |||||
node test/test2.js | |||||
``` | |||||
## 使用 | ## 使用 | ||||
```js | ```js | ||||
@@ -17,4 +23,6 @@ for (let index = 0; index < 5000; index++) { | |||||
} | } | ||||
``` | ``` | ||||
## 其他帮助 | |||||
在mysql中int类型最大长度是10位数字,由于本算法默认生成的是15位,最短也是11位,所以在mysql中需要使用bigint数据类型 |
@@ -0,0 +1,20 @@ | |||||
const GenId = require('..') | |||||
function test1() { | |||||
const genid = new GenId({ WorkerId: 1 }) | |||||
for (let index = 0; index < 5000; index++) { | |||||
console.log(genid.NextId()); | |||||
} | |||||
} | |||||
function test2() { | |||||
const genid = new GenId({ WorkerId: 1 }) | |||||
const id = genid.NextId() | |||||
console.log(typeof (id)) | |||||
console.log(id, id.toString().length) | |||||
} | |||||
function main() { | |||||
test2() | |||||
} | |||||
main() |
@@ -0,0 +1,3 @@ | |||||
.vscode | |||||
node_modules | |||||
env.config.js |
@@ -0,0 +1,32 @@ | |||||
# ❄ idgenerator-TypeScript | |||||
## 介绍 | |||||
项目更多介绍参照:https://github.com/yitter/idgenerator | |||||
代码贡献者:zhupengfei 在 bubao 布宝 基础上改版而来,感谢bubao 布宝 | |||||
执行测试代码 | |||||
```bash | |||||
ts-node test/test.ts | |||||
NODE_ENV=development REDIS_HOST=127.0.0.1 | |||||
``` | |||||
## 使用 | |||||
```js | |||||
import { Genid } from '../index' | |||||
let gen = new Genid({ WorkerId: 1 }) | |||||
let id1 = gen.NextId() | |||||
console.log(id1, id1.toString().length) | |||||
``` | |||||
@@ -0,0 +1,166 @@ | |||||
{ | |||||
"name": "cherry-id", | |||||
"version": "0.0.3", | |||||
"lockfileVersion": 1, | |||||
"requires": true, | |||||
"dependencies": { | |||||
"@cspotcode/source-map-consumer": { | |||||
"version": "0.8.0", | |||||
"resolved": "https://registry.nlark.com/@cspotcode/source-map-consumer/download/@cspotcode/source-map-consumer-0.8.0.tgz", | |||||
"integrity": "sha1-M79LeznBeIIWBvZpu8RHpqYpeGs=", | |||||
"dev": true | |||||
}, | |||||
"@cspotcode/source-map-support": { | |||||
"version": "0.6.1", | |||||
"resolved": "https://registry.nlark.com/@cspotcode/source-map-support/download/@cspotcode/source-map-support-0.6.1.tgz", | |||||
"integrity": "sha1-EYUR8xbi6H7kKUdhho4lTT2keWA=", | |||||
"dev": true, | |||||
"requires": { | |||||
"@cspotcode/source-map-consumer": "0.8.0" | |||||
} | |||||
}, | |||||
"@tsconfig/node10": { | |||||
"version": "1.0.8", | |||||
"resolved": "https://registry.nlark.com/@tsconfig/node10/download/@tsconfig/node10-1.0.8.tgz?cache=0&sync_timestamp=1623230253873&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40tsconfig%2Fnode10%2Fdownload%2F%40tsconfig%2Fnode10-1.0.8.tgz", | |||||
"integrity": "sha1-weToDW+WT77LM1nEO9SLQPfK2tk=", | |||||
"dev": true | |||||
}, | |||||
"@tsconfig/node12": { | |||||
"version": "1.0.9", | |||||
"resolved": "https://registry.nlark.com/@tsconfig/node12/download/@tsconfig/node12-1.0.9.tgz", | |||||
"integrity": "sha1-YsH23uLr2a6tgNw6+laBDljhoEw=", | |||||
"dev": true | |||||
}, | |||||
"@tsconfig/node14": { | |||||
"version": "1.0.1", | |||||
"resolved": "https://registry.nlark.com/@tsconfig/node14/download/@tsconfig/node14-1.0.1.tgz?cache=0&sync_timestamp=1623230252928&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40tsconfig%2Fnode14%2Fdownload%2F%40tsconfig%2Fnode14-1.0.1.tgz", | |||||
"integrity": "sha1-lfLRZ/+5uNIGiwsjUwL6/U33EfI=", | |||||
"dev": true | |||||
}, | |||||
"@tsconfig/node16": { | |||||
"version": "1.0.2", | |||||
"resolved": "https://registry.nlark.com/@tsconfig/node16/download/@tsconfig/node16-1.0.2.tgz", | |||||
"integrity": "sha1-Qjx3h30Fadsg4fyAiFrEEYMUAQ4=", | |||||
"dev": true | |||||
}, | |||||
"@types/json5": { | |||||
"version": "0.0.29", | |||||
"resolved": "https://registry.nlark.com/@types/json5/download/@types/json5-0.0.29.tgz", | |||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", | |||||
"dev": true | |||||
}, | |||||
"@types/node": { | |||||
"version": "16.7.6", | |||||
"resolved": "https://registry.nlark.com/@types/node/download/@types/node-16.7.6.tgz", | |||||
"integrity": "sha1-hmZHjbgJWqZuJbfkafPntT6ihV4=", | |||||
"dev": true | |||||
}, | |||||
"acorn": { | |||||
"version": "8.4.1", | |||||
"resolved": "https://registry.nlark.com/acorn/download/acorn-8.4.1.tgz?cache=0&sync_timestamp=1624526907659&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-8.4.1.tgz", | |||||
"integrity": "sha1-VsNiUfx8q8cJatwY8Fr+gUMhoow=", | |||||
"dev": true | |||||
}, | |||||
"acorn-walk": { | |||||
"version": "8.1.1", | |||||
"resolved": "https://registry.nlark.com/acorn-walk/download/acorn-walk-8.1.1.tgz", | |||||
"integrity": "sha1-Pdq3+E5KfiMT9sQUxbfayF9OPrw=", | |||||
"dev": true | |||||
}, | |||||
"arg": { | |||||
"version": "4.1.3", | |||||
"resolved": "https://registry.nlark.com/arg/download/arg-4.1.3.tgz", | |||||
"integrity": "sha1-Jp/HrVuOQstjyJbVZmAXJhwUQIk=", | |||||
"dev": true | |||||
}, | |||||
"cd": { | |||||
"version": "0.3.3", | |||||
"resolved": "https://registry.nlark.com/cd/download/cd-0.3.3.tgz", | |||||
"integrity": "sha1-EI7LV7/5O5a5EVxUVzz+8wCO+U0=", | |||||
"dev": true | |||||
}, | |||||
"create-require": { | |||||
"version": "1.1.1", | |||||
"resolved": "https://registry.npm.taobao.org/create-require/download/create-require-1.1.1.tgz", | |||||
"integrity": "sha1-wdfo8eX2z8n/ZfnNNS03NIdWwzM=", | |||||
"dev": true | |||||
}, | |||||
"diff": { | |||||
"version": "4.0.2", | |||||
"resolved": "https://registry.nlark.com/diff/download/diff-4.0.2.tgz", | |||||
"integrity": "sha1-YPOuy4nV+uUgwRqhnvwruYKq3n0=", | |||||
"dev": true | |||||
}, | |||||
"json5": { | |||||
"version": "1.0.1", | |||||
"resolved": "https://registry.nlark.com/json5/download/json5-1.0.1.tgz", | |||||
"integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", | |||||
"dev": true, | |||||
"requires": { | |||||
"minimist": "^1.2.0" | |||||
} | |||||
}, | |||||
"make-error": { | |||||
"version": "1.3.6", | |||||
"resolved": "https://registry.npm.taobao.org/make-error/download/make-error-1.3.6.tgz", | |||||
"integrity": "sha1-LrLjfqm2fEiR9oShOUeZr0hM96I=", | |||||
"dev": true | |||||
}, | |||||
"minimist": { | |||||
"version": "1.2.5", | |||||
"resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz?cache=0&sync_timestamp=1602337228360&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz", | |||||
"integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=", | |||||
"dev": true | |||||
}, | |||||
"strip-bom": { | |||||
"version": "3.0.0", | |||||
"resolved": "https://registry.npm.taobao.org/strip-bom/download/strip-bom-3.0.0.tgz?cache=0&sync_timestamp=1618599642133&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-bom%2Fdownload%2Fstrip-bom-3.0.0.tgz", | |||||
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", | |||||
"dev": true | |||||
}, | |||||
"ts-node": { | |||||
"version": "10.2.1", | |||||
"resolved": "https://registry.nlark.com/ts-node/download/ts-node-10.2.1.tgz?cache=0&sync_timestamp=1629307498234&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fts-node%2Fdownload%2Fts-node-10.2.1.tgz", | |||||
"integrity": "sha1-TMk76gp6uiF5SX5luwjd/BmLOrU=", | |||||
"dev": true, | |||||
"requires": { | |||||
"@cspotcode/source-map-support": "0.6.1", | |||||
"@tsconfig/node10": "^1.0.7", | |||||
"@tsconfig/node12": "^1.0.7", | |||||
"@tsconfig/node14": "^1.0.0", | |||||
"@tsconfig/node16": "^1.0.2", | |||||
"acorn": "^8.4.1", | |||||
"acorn-walk": "^8.1.1", | |||||
"arg": "^4.1.0", | |||||
"create-require": "^1.1.0", | |||||
"diff": "^4.0.1", | |||||
"make-error": "^1.1.1", | |||||
"yn": "3.1.1" | |||||
} | |||||
}, | |||||
"tsconfig-paths": { | |||||
"version": "3.11.0", | |||||
"resolved": "https://registry.nlark.com/tsconfig-paths/download/tsconfig-paths-3.11.0.tgz?cache=0&sync_timestamp=1629839735580&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftsconfig-paths%2Fdownload%2Ftsconfig-paths-3.11.0.tgz", | |||||
"integrity": "sha1-lUwf6XPaYznHjgawPOLkiBC2XzY=", | |||||
"dev": true, | |||||
"requires": { | |||||
"@types/json5": "^0.0.29", | |||||
"json5": "^1.0.1", | |||||
"minimist": "^1.2.0", | |||||
"strip-bom": "^3.0.0" | |||||
} | |||||
}, | |||||
"typescript": { | |||||
"version": "4.4.2", | |||||
"resolved": "https://registry.nlark.com/typescript/download/typescript-4.4.2.tgz?cache=0&sync_timestamp=1630011920137&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftypescript%2Fdownload%2Ftypescript-4.4.2.tgz", | |||||
"integrity": "sha1-bWGGQNQw41aaHftE99fmAM7T7oY=", | |||||
"dev": true | |||||
}, | |||||
"yn": { | |||||
"version": "3.1.1", | |||||
"resolved": "https://registry.nlark.com/yn/download/yn-3.1.1.tgz?cache=0&sync_timestamp=1628974764210&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fyn%2Fdownload%2Fyn-3.1.1.tgz", | |||||
"integrity": "sha1-HodAGgnXZ8HV6rJqbkwYUYLS61A=", | |||||
"dev": true | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
{ | |||||
"name": "cherry-id", | |||||
"version": "0.0.3", | |||||
"main": "index.ts", | |||||
"directories": { | |||||
"test": "test" | |||||
}, | |||||
"scripts": { | |||||
"test": "ts-node ./test/test.ts" | |||||
}, | |||||
"description": "", | |||||
"devDependencies": { | |||||
"@types/node": "^16.7.6", | |||||
"cd": "^0.3.3", | |||||
"ts-node": "^10.2.1", | |||||
"tsconfig-paths": "^3.11.0", | |||||
"typescript": "^4.4.2" | |||||
} | |||||
} |
@@ -0,0 +1,341 @@ | |||||
/** | |||||
* | |||||
*/ | |||||
export class snowflakeIdv1 { | |||||
/** | |||||
* 雪花计算方法,(1-漂移算法|2-传统算法),默认 1 | |||||
*/ | |||||
private Method | |||||
/** | |||||
* 基础时间(ms 单位),不能超过当前系统时间 | |||||
*/ | |||||
private BaseTime | |||||
/** | |||||
* 机器码,必须由外部设定,最大值 2^WorkerIdBitLength-1 | |||||
*/ | |||||
private WorkerId | |||||
/** | |||||
* 机器码位长,默认值 6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过 22) | |||||
*/ | |||||
private WorkerIdBitLength | |||||
/** | |||||
* 序列数位长,默认值 6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过 22) | |||||
*/ | |||||
private SeqBitLength | |||||
/** | |||||
* 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值 0,表示最大序列数取最大值(2^SeqBitLength-1]) | |||||
*/ | |||||
private MaxSeqNumber | |||||
/** | |||||
* 最小序列数(含),默认值 5,取值范围 [5, MaxSeqNumber],每毫秒的前 5 个序列数对应编号 0-4 是保留位,其中 1-4 是时间回拨相应预留位,0 是手工新值预留位 | |||||
*/ | |||||
private MinSeqNumber | |||||
/** | |||||
* 最大漂移次数(含),默认 2000,推荐范围 500-10000(与计算能力有关) | |||||
*/ | |||||
private TopOverCostCount | |||||
/** | |||||
* | |||||
*/ | |||||
private _TimestampShift | |||||
/** | |||||
* | |||||
*/ | |||||
private _CurrentSeqNumber | |||||
/** | |||||
* | |||||
*/ | |||||
private _LastTimeTick: bigint | |||||
/** | |||||
* 回拨次序, 支持 4 次回拨次序(避免回拨重叠导致 ID 重复) | |||||
*/ | |||||
private _TurnBackTimeTick: bigint | |||||
/** | |||||
* | |||||
*/ | |||||
private _TurnBackIndex | |||||
/** | |||||
* | |||||
*/ | |||||
private _IsOverCost | |||||
/** | |||||
* | |||||
*/ | |||||
private _OverCostCountInOneTerm | |||||
/** | |||||
*Creates an instance of Genid. | |||||
* @author bubao | |||||
* @param {{ | |||||
* Method: 1, // 雪花计算方法,(1-漂移算法|2-传统算法),默认 1 | |||||
* BaseTime: 1577836800000, // 基础时间(ms 单位),不能超过当前系统时间 | |||||
* WorkerId: Number, // 机器码,必须由外部设定,最大值 2^WorkerIdBitLength-1 | |||||
* WorkerIdBitLength: 6, // 机器码位长,默认值 6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过 22) | |||||
* SeqBitLength: 6, // 序列数位长,默认值 6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过 22) | |||||
* MaxSeqNumber: 5, // 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值 0,表示最大序列数取最大值(2^SeqBitLength-1]) | |||||
* MinSeqNumber: 5, // 最小序列数(含),默认值 5,取值范围 [5, MaxSeqNumber],每毫秒的前 5 个序列数对应编号 0-4 是保留位,其中 1-4 是时间回拨相应预留位,0 是手工新值预留位 | |||||
* TopOverCostCount: 2000// 最大漂移次数(含),默认 2000,推荐范围 500-10000(与计算能力有关) | |||||
* }} options | |||||
* @memberof Genid | |||||
*/ | |||||
constructor(options: any) { | |||||
if (options.WorkerId === undefined) | |||||
throw new Error("lost WorkerId") | |||||
// 1.BaseTime 2020年1月1日 | |||||
const BaseTime = 1577836800000 | |||||
if (!options.BaseTime || options.BaseTime < 0) | |||||
options.BaseTime = BaseTime | |||||
// 2.WorkerIdBitLength | |||||
const WorkerIdBitLength = 6 | |||||
if (!options.WorkerIdBitLength || options.WorkerIdBitLength < 0) | |||||
options.WorkerIdBitLength = WorkerIdBitLength | |||||
// 4.SeqBitLength | |||||
const SeqBitLength = 6 | |||||
if (!options.SeqBitLength || options.SeqBitLength < 0) | |||||
options.SeqBitLength = SeqBitLength | |||||
// 5.MaxSeqNumber | |||||
const MaxSeqNumber = (1 << SeqBitLength) - 1 | |||||
if (options.MaxSeqNumber <= 0 || options.MaxSeqNumber === undefined) { | |||||
options.MaxSeqNumber = MaxSeqNumber | |||||
} | |||||
// 6.MinSeqNumber | |||||
const MinSeqNumber = 5 | |||||
if (!options.MinSeqNumber || options.MinSeqNumber < 0) | |||||
options.MinSeqNumber = MinSeqNumber | |||||
// 7.Others | |||||
const topOverCostCount = 2000 | |||||
if (!options.TopOverCostCount || options.TopOverCostCount < 0) | |||||
options.TopOverCostCount = topOverCostCount | |||||
if (options.Method !== 2) | |||||
options.Method = 1 | |||||
else | |||||
options.Method = 2 | |||||
this.Method = BigInt(options.Method) | |||||
this.BaseTime = BigInt(options.BaseTime) | |||||
this.WorkerId = BigInt(options.WorkerId) | |||||
this.WorkerIdBitLength = BigInt(options.WorkerIdBitLength) | |||||
this.SeqBitLength = BigInt(options.SeqBitLength) | |||||
this.MaxSeqNumber = BigInt(options.MaxSeqNumber) | |||||
this.MinSeqNumber = BigInt(options.MinSeqNumber) | |||||
this.TopOverCostCount = BigInt(options.TopOverCostCount) | |||||
const timestampShift = this.WorkerIdBitLength + this.SeqBitLength | |||||
const currentSeqNumber = this.MinSeqNumber | |||||
this._TimestampShift = timestampShift | |||||
this._CurrentSeqNumber = currentSeqNumber | |||||
this._LastTimeTick = BigInt(0) | |||||
this._TurnBackTimeTick = BigInt(0) | |||||
this._TurnBackIndex = 0 | |||||
this._IsOverCost = false | |||||
this._OverCostCountInOneTerm = 0 | |||||
} | |||||
/** | |||||
* 当前序列号超过最大范围,开始透支使用序号号的通知事件,,本项暂未实现 | |||||
* @returns | |||||
*/ | |||||
private BeginOverCostAction(useTimeTick: any) { | |||||
} | |||||
/** | |||||
* 当前序列号超过最大范围,结束透支使用序号号的通知事件,,本项暂未实现 | |||||
* @returns | |||||
*/ | |||||
private EndOverCostAction(useTimeTick: any) { | |||||
// if m1._TermIndex > 10000 { | |||||
// m1._TermIndex = 0 | |||||
// } | |||||
} | |||||
/** | |||||
* 开始时间回拨通知,本项暂未实现 | |||||
* @returns | |||||
*/ | |||||
private BeginTurnBackAction(useTimeTick: any) { | |||||
} | |||||
/** | |||||
* 结束时间回拨通知,本项暂未实现 | |||||
* @returns | |||||
*/ | |||||
private EndTurnBackAction(useTimeTick: any) { | |||||
} | |||||
/** | |||||
* 雪花漂移算法 | |||||
* @returns | |||||
*/ | |||||
private NextOverCostId(): bigint { | |||||
const currentTimeTick = this.GetCurrentTimeTick() | |||||
if (currentTimeTick > this._LastTimeTick) { | |||||
this.EndOverCostAction(currentTimeTick) | |||||
//当前时间大于上次时间,说明是时间是递增的,这是正常情况 | |||||
this._LastTimeTick = currentTimeTick | |||||
this._CurrentSeqNumber = this.MinSeqNumber | |||||
this._IsOverCost = false | |||||
this._OverCostCountInOneTerm = 0 | |||||
// this._GenCountInOneTerm = 0 | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
if (this._OverCostCountInOneTerm >= this.TopOverCostCount) { | |||||
//当前漂移次数超过最大限制 | |||||
// TODO: 在漂移终止,等待时间对齐时,如果发生时间回拨较长,则此处可能等待较长时间。可优化为:在漂移终止时增加时间回拨应对逻辑。(该情况发生概率很低) | |||||
this.EndOverCostAction(currentTimeTick) | |||||
this._LastTimeTick = this.GetNextTimeTick() | |||||
this._CurrentSeqNumber = this.MinSeqNumber | |||||
this._IsOverCost = false | |||||
this._OverCostCountInOneTerm = 0 | |||||
// this._GenCountInOneTerm = 0 | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
if (this._CurrentSeqNumber > this.MaxSeqNumber) { | |||||
//当前序列数超过最大限制,则要提前透支 | |||||
this._LastTimeTick++ | |||||
this._CurrentSeqNumber = this.MinSeqNumber | |||||
this._IsOverCost = true | |||||
this._OverCostCountInOneTerm++ | |||||
// this._GenCountInOneTerm++ | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
// this._GenCountInOneTerm++ | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
/** | |||||
* 常规雪花算法 | |||||
* @returns | |||||
*/ | |||||
private NextNormalId() { | |||||
const currentTimeTick = this.GetCurrentTimeTick() | |||||
if (currentTimeTick < this._LastTimeTick) { | |||||
if (this._TurnBackTimeTick < 1) { | |||||
this._TurnBackTimeTick = this._LastTimeTick - BigInt(1) | |||||
this._TurnBackIndex++ | |||||
// 每毫秒序列数的前 5 位是预留位,0 用于手工新值,1-4 是时间回拨次序 | |||||
// 支持 4 次回拨次序(避免回拨重叠导致 ID 重复),可无限次回拨(次序循环使用)。 | |||||
if (this._TurnBackIndex > 4) | |||||
this._TurnBackIndex = 1 | |||||
this.BeginTurnBackAction(this._TurnBackTimeTick) | |||||
} | |||||
return this.CalcTurnBackId(this._TurnBackTimeTick) | |||||
} | |||||
// 时间追平时,_TurnBackTimeTick 清零 | |||||
if (this._TurnBackTimeTick > 0) { | |||||
this.EndTurnBackAction(this._TurnBackTimeTick) | |||||
this._TurnBackTimeTick = BigInt(0) | |||||
} | |||||
if (currentTimeTick > this._LastTimeTick) { | |||||
this._LastTimeTick = currentTimeTick | |||||
this._CurrentSeqNumber = this.MinSeqNumber | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
if (this._CurrentSeqNumber > this.MaxSeqNumber) { | |||||
this.BeginOverCostAction(currentTimeTick) | |||||
// this._TermIndex++ | |||||
this._LastTimeTick++ | |||||
this._CurrentSeqNumber = this.MinSeqNumber | |||||
this._IsOverCost = true | |||||
this._OverCostCountInOneTerm = 1 | |||||
// this._GenCountInOneTerm = 1 | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
return this.CalcId(this._LastTimeTick) | |||||
} | |||||
/** | |||||
* 生成ID | |||||
* @param useTimeTick 时间戳 | |||||
* @returns | |||||
*/ | |||||
private CalcId(useTimeTick: bigint) { | |||||
//ID组成 1.相对基础时间的时间差 | 2.WorkerId | 3.序列数 | |||||
//时间差,是生成ID时的系统时间减去 BaseTime 的总时间差(毫秒单位) | |||||
const result = BigInt(useTimeTick << this._TimestampShift) + BigInt(this.WorkerId << this.SeqBitLength) + BigInt(this._CurrentSeqNumber) | |||||
this._CurrentSeqNumber++ | |||||
return result | |||||
} | |||||
/** | |||||
* 生成时间回拨ID | |||||
* @returns | |||||
*/ | |||||
private CalcTurnBackId(useTimeTick: any) { | |||||
const result = BigInt(useTimeTick << this._TimestampShift) + BigInt(this.WorkerId << this.SeqBitLength) + BigInt(this._TurnBackIndex) | |||||
this._TurnBackTimeTick-- | |||||
return result | |||||
} | |||||
/** | |||||
* | |||||
* @returns | |||||
*/ | |||||
private GetCurrentTimeTick() { | |||||
const millis = BigInt((new Date()).valueOf()) | |||||
return millis - this.BaseTime | |||||
} | |||||
/** | |||||
* | |||||
* @returns | |||||
*/ | |||||
private GetNextTimeTick() { | |||||
let tempTimeTicker = this.GetCurrentTimeTick() | |||||
while (tempTimeTicker <= this._LastTimeTick) { | |||||
tempTimeTicker = this.GetCurrentTimeTick() | |||||
} | |||||
return tempTimeTicker | |||||
} | |||||
/** | |||||
* 生成ID | |||||
* @returns | |||||
*/ | |||||
public NextId(): number { | |||||
if (this.Method == BigInt(1)) { | |||||
//雪花漂移算法 | |||||
return parseInt(this.NextOverCostId().toString()) | |||||
} else { | |||||
//常规雪花算法 | |||||
return parseInt(this.NextNormalId().toString()) | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,33 @@ | |||||
import { snowflakeIdv1 } from '../snowflakeIdv1' | |||||
const WorkerId = process.env.WorkerId == undefined ? 1 : process.env.WorkerId | |||||
const Method = process.env.Method == undefined ? 1 : process.env.Method | |||||
console.log("WorkerId:" + WorkerId) | |||||
console.log("Method:" + Method) | |||||
console.log("--------------------") | |||||
let gen1 = new snowflakeIdv1({ WorkerId: WorkerId, Method: Method }) | |||||
function test1() { | |||||
let id1 = gen1.NextId() | |||||
console.log(id1, id1.toString().length) | |||||
} | |||||
function test2() { | |||||
let id1 = gen1.NextId() | |||||
console.log(typeof (id1)) | |||||
console.log(id1, id1.toString().length) | |||||
} | |||||
function main() { | |||||
test2() | |||||
} | |||||
main() | |||||
@@ -0,0 +1,76 @@ | |||||
{ | |||||
"compilerOptions": { | |||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */ | |||||
/* Basic Options */ | |||||
// "incremental": true, /* Enable incremental compilation */ | |||||
"target": "ES2020", | |||||
/* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ | |||||
"module": "commonjs", | |||||
/* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ | |||||
// "lib": [], /* Specify library files to be included in the compilation. */ | |||||
// "allowJs": true, /* Allow javascript files to be compiled. */ | |||||
// "checkJs": true, /* Report errors in .js files. */ | |||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ | |||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */ | |||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ | |||||
// "sourceMap": true, /* Generates corresponding '.map' file. */ | |||||
// "outFile": "./", /* Concatenate and emit output to single file. */ | |||||
// "outDir": "./", /* Redirect output structure to the directory. */ | |||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ | |||||
// "composite": true, /* Enable project compilation */ | |||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ | |||||
// "removeComments": true, /* Do not emit comments to output. */ | |||||
// "noEmit": true, /* Do not emit outputs. */ | |||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ | |||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | |||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ | |||||
/* Strict Type-Checking Options */ | |||||
"strict": true, | |||||
/* Enable all strict type-checking options. */ | |||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ | |||||
// "strictNullChecks": true, /* Enable strict null checks. */ | |||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */ | |||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ | |||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ | |||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ | |||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ | |||||
/* Additional Checks */ | |||||
// "noUnusedLocals": true, /* Report errors on unused locals. */ | |||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | |||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ | |||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ | |||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ | |||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ | |||||
/* Module Resolution Options */ | |||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ | |||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ | |||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ | |||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ | |||||
// "typeRoots": [], /* List of folders to include type definitions from. */ | |||||
// "types": [], /* Type declaration files to be included in compilation. */ | |||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | |||||
"esModuleInterop": true, | |||||
/* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | |||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | |||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | |||||
/* Source Map Options */ | |||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | |||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | |||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ | |||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | |||||
/* Experimental Options */ | |||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ | |||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ | |||||
/* Advanced Options */ | |||||
"skipLibCheck": true, | |||||
/* Skip type checking of declaration files. */ | |||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ | |||||
} | |||||
} |