From 2308932d258d99eab24fc69e98a1423782278b41 Mon Sep 17 00:00:00 2001 From: bonn Date: Tue, 31 Aug 2021 11:42:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0TypeScript=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=8A=E5=85=B6=E4=BB=96=E4=BC=98=E5=8C=96=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 基本可运行,待优化 * 增加注释 * 增加注释说明,更容易理解 * 提交package-lock.json文件,保证依赖版本一致 --- .../Yitter.IdGenerator/Core/SnowWorkerM1.cs | 74 ++-- Node.js/README.md | 8 + Node.js/test/test2.js | 20 + TypeScript/.gitignore | 3 + TypeScript/README.md | 32 ++ TypeScript/package-lock.json | 166 +++++++++ TypeScript/package.json | 19 + TypeScript/snowflakeIdv1.ts | 341 ++++++++++++++++++ TypeScript/test/test.ts | 33 ++ TypeScript/tsconfig.json | 76 ++++ 10 files changed, 735 insertions(+), 37 deletions(-) create mode 100644 Node.js/test/test2.js create mode 100644 TypeScript/.gitignore create mode 100644 TypeScript/README.md create mode 100644 TypeScript/package-lock.json create mode 100644 TypeScript/package.json create mode 100644 TypeScript/snowflakeIdv1.ts create mode 100644 TypeScript/test/test.ts create mode 100644 TypeScript/tsconfig.json diff --git a/C#.NET/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs b/C#.NET/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs index 1868e31..919e7e6 100644 --- a/C#.NET/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs +++ b/C#.NET/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs @@ -5,27 +5,27 @@ * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 * - */ - -using System; -using System.Threading; + */ + +using System; +using System.Threading; using System.Threading.Tasks; -namespace Yitter.IdGenerator -{ - /// - /// 雪花漂移算法 - /// - internal class SnowWorkerM1 : ISnowWorker +namespace Yitter.IdGenerator +{ + /// + /// 雪花漂移算法 + /// + internal class SnowWorkerM1 : ISnowWorker { /// /// 基础时间 /// - protected readonly DateTime BaseTime; + protected readonly DateTime BaseTime; - /// - /// 机器码 - /// + /// + /// 机器码 + /// protected readonly ushort WorkerId = 0; /// @@ -40,7 +40,7 @@ namespace Yitter.IdGenerator /// /// 最大序列数(含) - /// + /// protected readonly int MaxSeqNumber = 0; /// @@ -57,11 +57,11 @@ namespace Yitter.IdGenerator protected static object _SyncLock = new object(); 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 _GenCountInOneTerm = 0; protected int _TermIndex = 0; @@ -71,8 +71,8 @@ namespace Yitter.IdGenerator public Action GenAction { get; set; } - - public SnowWorkerM1(IdGeneratorOptions options) + + public SnowWorkerM1(IdGeneratorOptions options) { // 1.BaseTime if (options.BaseTime != DateTime.MinValue) @@ -117,17 +117,17 @@ namespace Yitter.IdGenerator MinSeqNumber = options.MinSeqNumber; // 7.Others - TopOverCostCount = options.TopOverCostCount; + TopOverCostCount = options.TopOverCostCount; if (TopOverCostCount == 0) { 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); }); - } + } private void BeginOverCostAction(in long useTimeTick) { @@ -338,11 +338,11 @@ namespace Yitter.IdGenerator 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() @@ -364,6 +364,6 @@ namespace Yitter.IdGenerator { return _IsOverCost ? NextOverCostId() : NextNormalId(); } - } - } -} + } + } +} diff --git a/Node.js/README.md b/Node.js/README.md index 493a39b..f4a8991 100644 --- a/Node.js/README.md +++ b/Node.js/README.md @@ -6,6 +6,12 @@ 代码贡献者:bubao 布宝 +执行测试代码 + +```bash +node test/test2.js +``` + ## 使用 ```js @@ -17,4 +23,6 @@ for (let index = 0; index < 5000; index++) { } ``` +## 其他帮助 +在mysql中int类型最大长度是10位数字,由于本算法默认生成的是15位,最短也是11位,所以在mysql中需要使用bigint数据类型 diff --git a/Node.js/test/test2.js b/Node.js/test/test2.js new file mode 100644 index 0000000..1aa6048 --- /dev/null +++ b/Node.js/test/test2.js @@ -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() \ No newline at end of file diff --git a/TypeScript/.gitignore b/TypeScript/.gitignore new file mode 100644 index 0000000..d8e9788 --- /dev/null +++ b/TypeScript/.gitignore @@ -0,0 +1,3 @@ +.vscode +node_modules +env.config.js \ No newline at end of file diff --git a/TypeScript/README.md b/TypeScript/README.md new file mode 100644 index 0000000..a30a043 --- /dev/null +++ b/TypeScript/README.md @@ -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) + +``` + + diff --git a/TypeScript/package-lock.json b/TypeScript/package-lock.json new file mode 100644 index 0000000..46ffb48 --- /dev/null +++ b/TypeScript/package-lock.json @@ -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 + } + } +} diff --git a/TypeScript/package.json b/TypeScript/package.json new file mode 100644 index 0000000..010c910 --- /dev/null +++ b/TypeScript/package.json @@ -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" + } +} diff --git a/TypeScript/snowflakeIdv1.ts b/TypeScript/snowflakeIdv1.ts new file mode 100644 index 0000000..eb1aff4 --- /dev/null +++ b/TypeScript/snowflakeIdv1.ts @@ -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()) + } + } +} + diff --git a/TypeScript/test/test.ts b/TypeScript/test/test.ts new file mode 100644 index 0000000..f41bd28 --- /dev/null +++ b/TypeScript/test/test.ts @@ -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() + + + + diff --git a/TypeScript/tsconfig.json b/TypeScript/tsconfig.json new file mode 100644 index 0000000..f1e6df9 --- /dev/null +++ b/TypeScript/tsconfig.json @@ -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. */ + } +} \ No newline at end of file -- GitLab