ee-core 2.0.3 → 2.1.0-beta.2

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.
Files changed (48) hide show
  1. package/config/config.default.js +9 -0
  2. package/const/channel.js +10 -1
  3. package/core/lib/ee.js +1 -1
  4. package/core/lib/loader/file_loader.js +2 -2
  5. package/core/lib/loader/mixin/config.js +1 -1
  6. package/core/lib/utils/index.js +1 -1
  7. package/ee/eeApp.js +8 -7
  8. package/exception/index.js +40 -12
  9. package/httpclient/index.js +4 -12
  10. package/index.js +1 -1
  11. package/jobs/child/app.js +11 -2
  12. package/jobs/child/forkProcess.js +81 -6
  13. package/jobs/child/index.js +41 -45
  14. package/jobs/child-pool/index.js +205 -0
  15. package/jobs/index.js +3 -1
  16. package/jobs/load-balancer/algorithm/index.js +12 -0
  17. package/jobs/load-balancer/algorithm/minimumConnection.js +19 -0
  18. package/jobs/load-balancer/algorithm/polling.js +12 -0
  19. package/jobs/load-balancer/algorithm/random.js +10 -0
  20. package/jobs/load-balancer/algorithm/specify.js +15 -0
  21. package/jobs/load-balancer/algorithm/weights.js +22 -0
  22. package/jobs/load-balancer/algorithm/weightsMinimumConnection.js +30 -0
  23. package/jobs/load-balancer/algorithm/weightsPolling.js +23 -0
  24. package/jobs/load-balancer/algorithm/weightsRandom.js +17 -0
  25. package/jobs/load-balancer/consts.js +10 -0
  26. package/jobs/load-balancer/index.js +202 -0
  27. package/jobs/load-balancer/scheduler.js +32 -0
  28. package/loader/index.js +22 -2
  29. package/message/childMessage.js +23 -0
  30. package/package.json +1 -6
  31. package/ps/index.js +44 -0
  32. package/utils/co.js +237 -0
  33. package/utils/depd/index.js +538 -0
  34. package/utils/depd/lib/browser/index.js +77 -0
  35. package/utils/extend.js +73 -0
  36. package/utils/get-port/index.d.ts +64 -0
  37. package/utils/get-port/index.js +109 -0
  38. package/utils/helper.js +25 -1
  39. package/utils/index.js +46 -0
  40. package/utils/ip.js +261 -0
  41. package/utils/is.js +2 -1
  42. package/utils/time/index.js +20 -0
  43. package/utils/time/ms.js +162 -0
  44. package/jobs/childPool/app.js +0 -62
  45. package/jobs/childPool/forkProcess.js +0 -81
  46. package/jobs/childPool/index.js +0 -71
  47. package/jobs/childPool/pool.js +0 -67
  48. /package/{oldUtils → old-utils}/index.js +0 -0
@@ -0,0 +1,205 @@
1
+ const EventEmitter = require('events');
2
+ const LoadBalancer = require('../load-balancer');
3
+ const Loader = require('../../loader');
4
+ const ForkProcess = require('../child/forkProcess');
5
+ const Channel = require('../../const/channel');
6
+ const Helper = require('../../utils/helper');
7
+
8
+ class ChildPoolJob extends EventEmitter {
9
+
10
+ constructor(opt = {}) {
11
+ super();
12
+ let options = Object.assign({
13
+ weights: [],
14
+ }, opt);
15
+
16
+ this.boundMap = new Map();
17
+ this.children = {};
18
+ this.min = 3;
19
+ this.max = 6;
20
+ this.strategy = 'polling';
21
+ this.weights = new Array(this.max).fill().map((v, i) => {
22
+ let w = Helper.validValue(options.weights[i]) ? options.weights[i] : 1
23
+ return w;
24
+ });
25
+
26
+ let lbOpt = {
27
+ algorithm: LoadBalancer.Algorithm.polling,
28
+ targets: [],
29
+ }
30
+ this.LB = new LoadBalancer(lbOpt);
31
+ this._initEvents();
32
+ }
33
+
34
+ /**
35
+ * 初始化监听
36
+ */
37
+ _initEvents() {
38
+ this.on(Channel.events.childProcessExit, (data) => {
39
+ this._removeChild(data.pid);
40
+ });
41
+ this.on(Channel.events.childProcessError, (data) => {
42
+ this._removeChild(data.pid);
43
+ });
44
+ }
45
+
46
+ /**
47
+ * 移除对象
48
+ */
49
+ _removeChild(pid) {
50
+ const length = Object.keys(this.children).length;
51
+ const lbOpt = {
52
+ id: pid,
53
+ weight: this.weights[length - 1],
54
+ }
55
+ this.LB.del(lbOpt);
56
+ delete this.children[pid];
57
+ }
58
+
59
+ /**
60
+ * 创建一个池子
61
+ */
62
+ async create(number = 3) {
63
+ if (number < 0 || number > this.max) {
64
+ throw new Error(`[ee-core] [jobs/child-pool] The number is invalid !`);
65
+ }
66
+ let currentNumber = this.children.length;
67
+ if (currentNumber > this.max) {
68
+ throw new Error(`[ee-core] [jobs/child-pool] The number of current processes number: ${currentNumber} is greater than the maximum: ${this.max} !`);
69
+ }
70
+
71
+ if (number + currentNumber > this.max) {
72
+ number = this.max - currentNumber;
73
+ }
74
+
75
+ // args
76
+ let options = Object.assign({
77
+ processArgs: {
78
+ type: 'childPoolJob'
79
+ }
80
+ }, {});
81
+ for (let i = 1; i <= number; i++) {
82
+ let task = new ForkProcess(this, options);
83
+ this._childCreated(task);
84
+ }
85
+
86
+ let pids = Object.keys(this.children);
87
+
88
+ return pids;
89
+ }
90
+
91
+ /**
92
+ * 子进程创建后处理
93
+ */
94
+ _childCreated(childProcess) {
95
+ let pid = childProcess.pid;
96
+ this.children[pid] = childProcess;
97
+
98
+ const length = Object.keys(this.children).length;
99
+ let lbTask = {
100
+ id: pid,
101
+ weight: this.weights[length - 1],
102
+ }
103
+ this.LB.add(lbTask);
104
+ }
105
+
106
+ /**
107
+ * 执行一个job文件
108
+ */
109
+ run(filepath, params = {}) {
110
+ const jobPath = Loader.getFullpath(filepath);
111
+ const childProcess = this.getChild();
112
+ childProcess.dispatch('run', jobPath, params);
113
+
114
+ return childProcess;
115
+ }
116
+
117
+ /**
118
+ * 异步执行一个job文件
119
+ */
120
+ async runPromise(filepath, params = {}) {
121
+ return this.run(filepath, params);
122
+ }
123
+
124
+ /**
125
+ * 获取绑定的进程对象
126
+ */
127
+ getBoundChild(boundId) {
128
+ let proc;
129
+ const boundPid = this.boundMap.get(boundId);
130
+ if (boundPid) {
131
+ proc = this.children[boundPid];
132
+ return proc;
133
+ }
134
+
135
+ // 获取进程并绑定
136
+ proc = this.getChild();
137
+ this.boundMap.set(boundId, proc.pid);
138
+
139
+ return proc;
140
+ }
141
+
142
+ /**
143
+ * 通过pid获取一个子进程对象
144
+ */
145
+ getChildByPid(pid) {
146
+ let proc = this.children[pid] || null;
147
+ return proc;
148
+ }
149
+
150
+ /**
151
+ * 获取一个子进程对象
152
+ */
153
+ getChild() {
154
+ let proc;
155
+ const currentPids = Object.keys(this.children);
156
+
157
+ // 没有则创建
158
+ if (currentPids.length == 0) {
159
+ let subIds = this.create(1);
160
+ proc = this.children[subIds[0]];
161
+ } else {
162
+ // 从池子中获取一个
163
+ let onePid = this.LB.pickOne().id;
164
+ proc = this.children[onePid];
165
+ }
166
+
167
+ if (!proc) {
168
+ let errorMessage = `[ee-core] [jobs/child-pool] Failed to obtain the child process !`
169
+ throw new Error(errorMessage);
170
+ }
171
+
172
+ return proc;
173
+ }
174
+
175
+ /**
176
+ * 获取当前pids
177
+ */
178
+ getPids() {
179
+ let pids = Object.keys(this.children);
180
+ return pids;
181
+ }
182
+
183
+ /**
184
+ * kill all
185
+ * @param type {String} - 'sequence' | 'parallel'
186
+ */
187
+ killAll(type = 'parallel') {
188
+ let i = 1;
189
+ Object.keys(this.children).forEach(key => {
190
+ let proc = this.children[key];
191
+ if (proc) {
192
+ if (type == 'sequence') {
193
+ setTimeout(()=>{
194
+ proc.kill();
195
+ }, i * 1000)
196
+ i++;
197
+ } else {
198
+ proc.kill();
199
+ }
200
+ }
201
+ });
202
+ }
203
+ }
204
+
205
+ module.exports = ChildPoolJob;
package/jobs/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  const ChildJob = require('./child');
2
2
  const RendererJob = require('./renderer');
3
+ const ChildPoolJob = require('./child-pool');
3
4
 
4
5
  module.exports = {
5
6
  ChildJob,
6
- RendererJob
7
+ RendererJob,
8
+ ChildPoolJob
7
9
  };
@@ -0,0 +1,12 @@
1
+ const Consts = require("../consts");
2
+
3
+ module.exports = {
4
+ [Consts.polling]: require('./polling'),
5
+ [Consts.weights]: require('./weights'),
6
+ [Consts.random]: require('./random'),
7
+ [Consts.specify]: require('./specify'),
8
+ [Consts.minimumConnection]: require('./minimumConnection'),
9
+ [Consts.weightsPolling]: require('./weightsPolling'),
10
+ [Consts.weightsRandom]: require('./weightsRandom'),
11
+ [Consts.weightsMinimumConnection]: require('./weightsMinimumConnection'),
12
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 最小连接数
3
+ */
4
+ module.exports = function (tasks, conMap={}) {
5
+ if (tasks.length < 2) return tasks[0] || null;
6
+
7
+ let min = conMap[tasks[0].id];
8
+ let minIndex = 0;
9
+
10
+ for (let i = 1; i < tasks.length; i++) {
11
+ const con = conMap[tasks[i].id] || 0;
12
+ if (con <= min) {
13
+ min = con;
14
+ minIndex = i;
15
+ }
16
+ }
17
+
18
+ return tasks[minIndex] || null;
19
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 轮询
3
+ */
4
+ module.exports = function (tasks, currentIndex, context) {
5
+ if (!tasks.length) return null;
6
+
7
+ const task = tasks[currentIndex];
8
+ context.currentIndex ++;
9
+ context.currentIndex %= tasks.length;
10
+
11
+ return task || null;
12
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 随机
3
+ */
4
+ module.exports = function (tasks) {
5
+
6
+ const length = tasks.length;
7
+ const target = tasks[Math.floor(Math.random() * length)];
8
+
9
+ return target || null;
10
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 声明绑定
3
+ */
4
+ module.exports = function (tasks, id) {
5
+ let task;
6
+
7
+ for (let i = 0; i < tasks.length; i++) {
8
+ if (tasks[i].id === id) {
9
+ task = tasks[i];
10
+ break;
11
+ }
12
+ }
13
+
14
+ return task || null;
15
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 权重
3
+ */
4
+ module.exports = function (tasks, weightTotal, context) {
5
+
6
+ if (!tasks.length) return null;
7
+
8
+ let max = tasks[0].weight, maxIndex = 0, sum;
9
+
10
+ for (let i = 0; i < tasks.length; i++) {
11
+ sum = (tasks[i].weight || 0) + Math.random() * weightTotal;
12
+ if (sum >= max) {
13
+ max = sum;
14
+ maxIndex = i;
15
+ }
16
+ }
17
+
18
+ context.weightIndex += 1;
19
+ context.weightIndex %= (weightTotal + 1);
20
+
21
+ return tasks[maxIndex];
22
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 权重最小连接数
3
+ */
4
+ module.exports = function (tasks, weightTotal, connectionsMap, context) {
5
+
6
+ if (!tasks.length) return null;
7
+
8
+ let min = tasks[0].weight, minIndex = 0, sum;
9
+
10
+ const connectionsTotal = tasks.reduce((total, cur) => {
11
+ total += (connectionsMap[cur.id] || 0);
12
+ return total;
13
+ }, 0);
14
+
15
+ // algorithm: (weight + connections'weight) + random factor
16
+ for (let i = 0; i < tasks.length; i++) {
17
+ sum =
18
+ (tasks[i].weight || 0) + (Math.random() * weightTotal) +
19
+ (( (connectionsMap[tasks[i].id] || 0) * weightTotal ) / connectionsTotal);
20
+ if (sum <= min) {
21
+ min = sum;
22
+ minIndex = i;
23
+ }
24
+ }
25
+
26
+ context.weightIndex += 1;
27
+ context.weightIndex %= (weightTotal + 1);
28
+
29
+ return tasks[minIndex];
30
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 权重轮询
3
+ */
4
+ module.exports = function (tasks, weightIndex, weightTotal, context) {
5
+
6
+ if (!tasks.length) return null;
7
+
8
+ let weight = 0;
9
+ let task;
10
+
11
+ for (let i = 0; i < tasks.length; i++) {
12
+ weight += tasks[i].weight || 0;
13
+ if (weight > weightIndex) {
14
+ task = tasks[i];
15
+ break;
16
+ }
17
+ }
18
+
19
+ context.weightIndex += 1;
20
+ context.weightIndex %= (weightTotal + 1);
21
+
22
+ return task;
23
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 权重随机
3
+ */
4
+ module.exports = function (tasks, weightTotal) {
5
+ let task;
6
+ let weight = Math.ceil(Math.random() * weightTotal);
7
+
8
+ for (let i = 0; i < tasks.length; i++) {
9
+ weight -= tasks[i].weight || 0;
10
+ if (weight <= 0) {
11
+ task = tasks[i];
12
+ break;
13
+ }
14
+ }
15
+
16
+ return task || null;
17
+ };
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ polling: 'polling', // 轮询
3
+ weights: 'weights', // 权重
4
+ random: 'random', // 随机
5
+ specify: 'specify', // 声明绑定
6
+ weightsPolling: 'weightsPolling', // 权重轮询
7
+ weightsRandom: 'weightsRandom', // 权重随机
8
+ minimumConnection: 'minimumConnection', // 最小连接数
9
+ weightsMinimumConnection: 'weightsMinimumConnection', // 权重最小连接数
10
+ };
@@ -0,0 +1,202 @@
1
+ const Consts = require("./consts");
2
+ const Scheduler = require("./scheduler");
3
+
4
+ /**
5
+ * 负载均衡器
6
+ * @intro 参考electron-re项目,并做了一些改动
7
+ * @since 1.0.0
8
+ */
9
+ class LoadBalancer {
10
+
11
+ static Algorithm = Consts;
12
+
13
+ /**
14
+ * @param {Object} options
15
+ * @param {Array } options.targets [ targets for load balancing calculation: [{id: 1, weight: 1}, {id: 2, weight: 2}] ]
16
+ * @param {String} options.algorithm
17
+ */
18
+ constructor(options) {
19
+ this.targets = options.targets;
20
+ this.algorithm = options.algorithm || Consts.polling;
21
+ this.params = { // data for algorithm
22
+ currentIndex: 0, // index
23
+ weightIndex: 0, // index for weight alogrithm
24
+ weightTotal: 0, // total weight
25
+ connectionsMap: {}, // connections of each target
26
+ cpuOccupancyMap: {}, // cpu occupancy of each target
27
+ memoryOccupancyMap: {}, // cpu occupancy of each target
28
+ };
29
+ this.scheduler = new Scheduler(this.algorithm);
30
+ this.memoParams = this.memorizedParams();
31
+ this.calculateWeightIndex();
32
+ }
33
+
34
+ /**
35
+ * 算法参数
36
+ */
37
+ memorizedParams() {
38
+ return {
39
+ [Consts.random]: () => [],
40
+ [Consts.polling]: () => [this.params.currentIndex, this.params],
41
+ [Consts.weights]: () => [this.params.weightTotal, this.params],
42
+ [Consts.specify]: (id) => [id],
43
+ [Consts.weightsRandom]: () => [this.params.weightTotal],
44
+ [Consts.weightsPolling]: () => [this.params.weightIndex, this.params.weightTotal, this.params],
45
+ [Consts.minimumConnection]: () => [this.params.connectionsMap],
46
+ [Consts.weightsMinimumConnection]: () => [this.params.weightTotal, this.params.connectionsMap, this.params],
47
+ };
48
+ }
49
+
50
+ /**
51
+ * 刷新参数
52
+ */
53
+ refreshParams(pidMap) {
54
+ const infos = Object.values(pidMap);
55
+ for (let info of infos) {
56
+ // this.params.connectionsMap[id] = connections;
57
+ this.params.cpuOccupancyMap[info.pid] = info.cpu;
58
+ this.params.memoryOccupancyMap[info.pid] = info.memory;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 选举出一个进程
64
+ */
65
+ pickOne(...params) {
66
+ return this.scheduler.calculate(
67
+ this.targets, this.memoParams[this.algorithm](...params)
68
+ );
69
+ }
70
+
71
+ /**
72
+ * 选举出多个进程
73
+ */
74
+ pickMulti(count = 1, ...params) {
75
+ return new Array(count).fill().map(
76
+ () => this.pickOne(...params)
77
+ );
78
+ }
79
+
80
+ /**
81
+ * 计算权重
82
+ */
83
+ calculateWeightIndex() {
84
+ this.params.weightTotal = this.targets.reduce((total, cur) => total + (cur.weight || 0), 0);
85
+ if (this.params.weightIndex > this.params.weightTotal) {
86
+ this.params.weightIndex = this.params.weightTotal;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * 计算索引
92
+ */
93
+ calculateIndex() {
94
+ if (this.params.currentIndex >= this.targets.length) {
95
+ this.params.currentIndex = (this.params.currentIndex - 1 >= 0) ? (this.params.currentIndex - 1) : 0;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 清除data
101
+ */
102
+ clean(id) {
103
+ if (id) {
104
+ delete this.params.connectionsMap[id];
105
+ delete this.params.cpuOccupancyMap[id];
106
+ delete this.params.memoryOccupancyMap[id];
107
+ } else {
108
+ this.params = {
109
+ currentIndex: 0,
110
+ connectionsMap: {},
111
+ cpuOccupancyMap: {},
112
+ memoryOccupancyMap: {},
113
+ };
114
+ }
115
+ }
116
+
117
+ /**
118
+ * 添加一个进程信息
119
+ */
120
+ add(task) {
121
+ if (this.targets.find(target => target.id === task.id)) {
122
+ return console.warn(`Add Operation: the task ${task.id} already exists.`);
123
+ }
124
+ this.targets.push(task);
125
+ this.calculateWeightIndex();
126
+ }
127
+
128
+ /**
129
+ * 删除一个进程信息
130
+ */
131
+ del(target) {
132
+ let found = false;
133
+ for (let i = 0; i < this.targets.length; i++) {
134
+ if (this.targets[i].id === target.id) {
135
+ this.targets.splice(i, 1);
136
+ this.clean(target.id);
137
+ this.calculateIndex();
138
+ found = true;
139
+ break;
140
+ }
141
+ }
142
+
143
+ if (found) {
144
+ this.calculateWeightIndex();
145
+ } else {
146
+ console.warn(`Del Operation: the task ${target.id} is not found.`, this.targets);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * 擦除
152
+ */
153
+ wipe() {
154
+ this.targets = [];
155
+ this.calculateWeightIndex();
156
+ this.clean();
157
+ }
158
+
159
+ /**
160
+ * 更新计算参数
161
+ */
162
+ updateParams(object) {
163
+ Object.entries(object).map(([key, value]) => {
164
+ if (key in this.params) {
165
+ this.params[key] = value;
166
+ }
167
+ });
168
+ }
169
+
170
+ /**
171
+ * 设置targets
172
+ */
173
+ setTargets(targets) {
174
+ const targetsMap = targets.reduce((total, cur) => {
175
+ total[cur.id] = 1;
176
+ return total;
177
+ }, {});
178
+ this.targets.forEach(target => {
179
+ if (!(target.id in targetsMap)) {
180
+ this.clean(target.id);
181
+ this.calculateIndex();
182
+ }
183
+ });
184
+ this.targets = targets;
185
+ this.calculateWeightIndex();
186
+ }
187
+
188
+ /**
189
+ * 设置算法
190
+ */
191
+ setAlgorithm = (algorithm) => {
192
+ if (algorithm in Consts) {
193
+ this.algorithm = algorithm;
194
+ this.params.weightIndex = 0;
195
+ this.scheduler.setAlgorithm(this.algorithm);
196
+ } else {
197
+ throw new Error(`Invalid algorithm: ${algorithm}, pick from ${Object.keys(Consts).join('|')}`);
198
+ }
199
+ }
200
+ }
201
+
202
+ module.exports = LoadBalancer;
@@ -0,0 +1,32 @@
1
+ const Consts = require("./consts");
2
+ const algorithm = require('./algorithm');
3
+
4
+ /**
5
+ * 算法调度器
6
+ */
7
+ class Scheduler {
8
+ constructor(algorithm) {
9
+ this.algorithm = algorithm || Consts.polling;
10
+ }
11
+
12
+ /**
13
+ * 计算
14
+ */
15
+ calculate(tasks, params) {
16
+ const results = algorithm[this.algorithm](tasks, ...params);
17
+ return results;
18
+ }
19
+
20
+ /**
21
+ * 设置算法
22
+ */
23
+ setAlgorithm = (algorithm) => {
24
+ if (algorithm in Consts) {
25
+ this.algorithm = algorithm;
26
+ } else {
27
+ throw new Error(`Invalid algorithm: ${algorithm}, pick from ${Object.keys(Consts).join('|')}`);
28
+ }
29
+ }
30
+ }
31
+
32
+ module.exports = Scheduler;
package/loader/index.js CHANGED
@@ -127,7 +127,7 @@ module.exports = {
127
127
  const ret = UtilsCore.loadFile(fullpath);
128
128
 
129
129
  return ret;
130
- },
130
+ },
131
131
 
132
132
  /**
133
133
  * 加载jobs模块(子进程中使用)
@@ -140,7 +140,27 @@ module.exports = {
140
140
  const ret = this.requireModule(filepath, 'jobs');
141
141
 
142
142
  return ret;
143
- },
143
+ },
144
+
145
+ /**
146
+ * 获取electron目录下文件的绝对路径
147
+ * @param {String} filepath - fullpath
148
+ */
149
+ getFullpath(filepath) {
150
+ let fullpath;
151
+ const isAbsolute = path.isAbsolute(filepath);
152
+ if (!isAbsolute) {
153
+ filepath = path.join(Ps.getBaseDir(), filepath);
154
+ }
155
+
156
+ fullpath = this.resolveModule(filepath);
157
+ if (!fs.existsSync(fullpath)) {
158
+ throw new Error(`[ee-core] [loader] getFullpath filepath ${fullpath} not exists`);
159
+ }
160
+
161
+ return fullpath;
162
+ }
163
+
144
164
  }
145
165
 
146
166