未验证 提交 8ff4e433 编写于 作者: 布宝 提交者: GitHub

Node (#1)

* Node

* add readme

* add .gitignore

* update cherry id
上级 e6c04260
{
"eggHelper.serverPort": 35684
}
\ No newline at end of file
.vscode
node_modules
package-lock.json
env.config.js
\ No newline at end of file
<!--
* @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());
}
```
/**
* @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;
\ No newline at end of file
{
"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": ""
}
\ No newline at end of file
/**
* @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();
}
});
\ No newline at end of file
/**
* @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();
})();
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册