Browse Source

Node (#1)

* Node

* add readme

* add .gitignore

* update cherry id
tags/v1.2
布宝 GitHub 3 years ago
parent
commit
8ff4e43338
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 344 additions and 0 deletions
  1. +3
    -0
      .vscode/settings.json
  2. +4
    -0
      NodeJs/.gitignore
  3. +26
    -0
      NodeJs/README.md
  4. +214
    -0
      NodeJs/index.js
  5. +22
    -0
      NodeJs/package.json
  6. +54
    -0
      NodeJs/test/process_test.js
  7. +21
    -0
      NodeJs/test/test.js

+ 3
- 0
.vscode/settings.json View File

@@ -0,0 +1,3 @@
{
"eggHelper.serverPort": 35684
}

+ 4
- 0
NodeJs/.gitignore View File

@@ -0,0 +1,4 @@
.vscode
node_modules
package-lock.json
env.config.js

+ 26
- 0
NodeJs/README.md View File

@@ -0,0 +1,26 @@
<!--
* @description: README
* @author: bubao
* @date: 2021-04-28 09:52:24
* @last author: bubao
* @last edit time: 2021-04-28 10:34:31
-->

# cherry-id

[![NPM version](https://img.shields.io/npm/v/cherry-id.svg)](https://www.npmjs.com/package/cherry-id) [![jaywcjlove/sb](https://jaywcjlove.github.io/sb/lang/english.svg)](README.md)

该代码参考 go 版本,需要 Nodejs 或者浏览器支持 `BigInt`。基本参数与 go 版本一致,只要,但是只实现了雪花飘移算法,没有传统的雪花算法,所以`Method`参数并没有作用。

## 使用

```js
const GenId = require("./index.js")
const genid = new GenId({ WorkerId: 1 });

for (let index = 0; index < 5000; index++) {
console.log(genid.NextId());
}
```



+ 214
- 0
NodeJs/index.js View File

@@ -0,0 +1,214 @@
/**
* @description:
* @author: bubao
* @date: 2021-04-27 17:19:51
* @last author: bubao
* @last edit time: 2021-04-27 23:17:06
*/
class Genid {
/**
*Creates an instance of Genid.
* @author bubao
* @date 2021-04-27
* @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) {
if (options.WorkerId === undefined) {
throw new Error("lost WorkerId");
}
// 1.BaseTime
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 = 0;
this._TurnBackTimeTick = 0;
this._TurnBackIndex = 0;
this._IsOverCost = false;
this._OverCostCountInOneTerm = 0;
}

// DoGenIDAction .
DoGenIdAction(OverCostActionArg) { }

BeginOverCostAction(useTimeTick) { }

EndOverCostAction(useTimeTick) {
// if m1._TermIndex > 10000 {
// m1._TermIndex = 0
// }
}

BeginTurnBackAction(useTimeTick) { }

EndTurnBackAction(useTimeTick) { }

NextOverCostId() {
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) {
// 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);
}

NextNormalId() {
const currentTimeTick = this.GetCurrentTimeTick();
if (currentTimeTick < this._LastTimeTick) {
if (this._TurnBackTimeTick < 1) {
this._TurnBackTimeTick = this._LastTimeTick - 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 = 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);
}

CalcId(useTimeTick) {
const result = BigInt(useTimeTick << this._TimestampShift) + BigInt(this.WorkerId << this.SeqBitLength) + BigInt(this._CurrentSeqNumber);
this._CurrentSeqNumber++;
return result;
}

CalcTurnBackId(useTimeTick) {
const result = BigInt(useTimeTick << this._TimestampShift) + BigInt(this.WorkerId << this.SeqBitLength) + BigInt(this._TurnBackIndex);
this._TurnBackTimeTick--;
return result;
}

GetCurrentTimeTick() {
const millis = BigInt((new Date()).valueOf());
return millis - this.BaseTime;
}

GetNextTimeTick() {
let tempTimeTicker = this.GetCurrentTimeTick();
while (tempTimeTicker <= this._LastTimeTick) {
tempTimeTicker = this.GetCurrentTimeTick();
}
return tempTimeTicker;
}

NextId() {
if (this._IsOverCost) {
return parseInt(this.NextOverCostId());
} else {
return parseInt(this.NextNormalId());
}
}
}

module.exports = Genid;

+ 22
- 0
NodeJs/package.json View File

@@ -0,0 +1,22 @@
{
"name": "cherry-id",
"version": "0.0.3",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "node ./test/test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/bubao/cherry-id-js.git"
},
"author": "bubao",
"license": "MIT",
"bugs": {
"url": "https://github.com/bubao/cherry-id-js/issues"
},
"homepage": "https://github.com/bubao/cherry-id-js#readme",
"description": ""
}

+ 54
- 0
NodeJs/test/process_test.js View File

@@ -0,0 +1,54 @@
/**
* @description:
* @author: bubao
* @date: 2021-04-27 23:38:30
* @last author: bubao
* @last edit time: 2021-04-28 10:35:20
*/
const Redis = require("ioredis");
const { spawn } = require("child_process");
const config = require("../env.config.js");
const redis = new Redis(config);
//保存被子进程实例数组
var workers = {};

//这里的被子进程理论上可以无限多
// var appsPath = [__dirname+'/service/clickService.js',__dirname+'/service/showService.js'];

var createWorker = function (appPath, i) {

//保存spawn返回的进程实例
var worker = spawn('node', [appPath, i]);

//监听子进程exit事件
worker.on('exit', async function () {
console.info('worker:' + worker.pid + 'exited');
delete workers[worker.pid];
// createWorker(appPath);
if (Object.keys(workers).length === 0) {
console.log(await redis.scard('setTest'));
await redis.del("setTest");
redis.end();
}
});
workers[worker.pid] = worker;
console.info('create worker:' + worker.pid);
};
redis.del("setTest").then(() => {
//启动所有子进程
for (var i = 10; i > 0; i--) {
createWorker(__dirname + '/test.js', i);
}
});
//父进程退出时杀死所有子进程
process.on('exit', async function () {
console.info('parent exit.');
for (var pid in workers) {
workers[pid].kill('SIGHUP');
}
if (Object.keys(workers).length===0&&redis.status!=="end") {
console.log(await redis.scard('setTest'));
await redis.del("setTest");
redis.end();
}
});

+ 21
- 0
NodeJs/test/test.js View File

@@ -0,0 +1,21 @@
/**
* @description:
* @author: bubao
* @date: 2021-04-27 17:23:36
* @last author: bubao
* @last edit time: 2021-04-28 10:34:24
*/

const GenId = require('..')
const Redis = require("ioredis");
const config = require("../env.config.js");
const redis = new Redis(config);

const genid = new GenId({ WorkerId: (process.argv[2] || 1) - 0 });

(async () => {
for (let index = 0; index < 5000; index++) {
await redis.sadd("setTest", genid.NextId());
}
redis.end();
})();

Loading…
Cancel
Save