multi-tasks 1.2.8 → 2.0.0
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.
- package/README.md +79 -40
- package/cmd.js +42 -0
- package/examples/example0/run-readme.js +88 -0
- package/examples/example0/run.js +32 -17
- package/index.js +1 -34
- package/monitors/cli.js +28 -0
- package/package.json +2 -1
- package/todo.txt +4 -0
- package/utils/asyncQueue.js +32 -0
- package/utils/backward-v1.js +11 -0
- package/utils/makedir.js +27 -1
- package/utils/randoms.js +15 -0
- package/utils/test/test_asyncqueue.js +116 -0
- package/workers/Multithread.js +175 -0
- package/workers/TaskMgr.js +186 -0
- package/workers/WorkerMgr.js +31 -0
- package/workers/asMaster.js +71 -0
- package/workers/asWorker.js +14 -0
- package/workers/helper.js +22 -0
- package/workers/index.js +8 -0
- package/workers/SuperConsumer.js +0 -106
- package/workers/SuperProducer.js +0 -97
- package/workers/master.js +0 -171
- package/workers/report/report_status.js +0 -52
- package/workers/taskSplitter.js +0 -30
- package/workers/test/MyConsumer.js +0 -25
- package/workers/test/MyProducer.js +0 -26
- package/workers/test/test_master.js +0 -12
- package/workers/test/test_producer.js +0 -4
- package/workers/test/test_splitter.js +0 -7
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
var fs = require('fs');
|
|
2
|
+
var pathutil = require('path');
|
|
3
|
+
const cluster = require('cluster');
|
|
4
|
+
const numCPUs = require('os').cpus().length;
|
|
5
|
+
|
|
6
|
+
let MSG_REQUEST_NEW_TASK = 'request_new_task';
|
|
7
|
+
let MSG_NO_TASK_FOUND = 'no_task_found';
|
|
8
|
+
|
|
9
|
+
const TaskMgr = require('./TaskMgr');
|
|
10
|
+
const WorkerMgr = require('./WorkerMgr');
|
|
11
|
+
const helper = require('./helper');
|
|
12
|
+
|
|
13
|
+
let fmtdigit = (n)=>{
|
|
14
|
+
return n > 9 ? "" + n: "0" + n;
|
|
15
|
+
};
|
|
16
|
+
let getDateTimeTxt = ()=>{
|
|
17
|
+
let d=new Date();
|
|
18
|
+
let YYYY=d.getFullYear()
|
|
19
|
+
let MM=fmtdigit(d.getMonth()+1);
|
|
20
|
+
let DD=fmtdigit(d.getDate());
|
|
21
|
+
let HH=fmtdigit(d.getHours());
|
|
22
|
+
let mm=fmtdigit(d.getMinutes());
|
|
23
|
+
let ss=fmtdigit(d.getSeconds());
|
|
24
|
+
//return moment().format('YYYY-MM-DD HH:mm:ss');
|
|
25
|
+
return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let USER_CONFIG;
|
|
29
|
+
|
|
30
|
+
let taskCount = 0;
|
|
31
|
+
|
|
32
|
+
const start = (config)=>{
|
|
33
|
+
USER_CONFIG = config;//this will remain user defined functions
|
|
34
|
+
let isMaster = (cluster.isPrimary || cluster.isMaster);
|
|
35
|
+
|
|
36
|
+
if (isMaster) {//init
|
|
37
|
+
console.log('[Master]: Start!!');
|
|
38
|
+
let PID = process.pid;
|
|
39
|
+
let numOfWorkers = config.numberOfWorkers;
|
|
40
|
+
console.log(` numCPUs=${numCPUs}`);
|
|
41
|
+
console.log(` numOfWorkers=${numOfWorkers}`);
|
|
42
|
+
console.log(` PID=${PID}`);
|
|
43
|
+
|
|
44
|
+
const AsyncQueue = require(`../utils/asyncQueue`);
|
|
45
|
+
const queue = new AsyncQueue();
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
require('./asMaster').init(config).then((updated_config)=>{
|
|
49
|
+
console.log(` new tasks=${TaskMgr.getTasks('new').length}`);
|
|
50
|
+
config = updated_config;
|
|
51
|
+
for (let i = 0; i < numOfWorkers; i++) {
|
|
52
|
+
let worker = cluster.fork();
|
|
53
|
+
WorkerMgr.addWorker(worker);
|
|
54
|
+
worker.on('message', function(message) {
|
|
55
|
+
if(message === MSG_REQUEST_NEW_TASK){
|
|
56
|
+
queue.enqueue(TaskMgr.updateNewTasks);//use queue to make sure no I/O conflict
|
|
57
|
+
queue.enqueue(async () => {
|
|
58
|
+
TaskMgr.popNewTask().then((subTask)=>{
|
|
59
|
+
if(!subTask){
|
|
60
|
+
//no tasks left
|
|
61
|
+
console.log('[Master]: no tasks left!');
|
|
62
|
+
//console.log('workers', cluster.workers);
|
|
63
|
+
if(config.autoCloseAfterCompletion){
|
|
64
|
+
worker.kill();
|
|
65
|
+
}else{//waiting for new tasks
|
|
66
|
+
worker.send(MSG_NO_TASK_FOUND,()=>{}, {
|
|
67
|
+
keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//
|
|
71
|
+
}else{
|
|
72
|
+
//send new tasks to the worker
|
|
73
|
+
worker.send({
|
|
74
|
+
taskCount,
|
|
75
|
+
startTimestamp: new Date()*1,
|
|
76
|
+
config,
|
|
77
|
+
subTask
|
|
78
|
+
},()=>{}, {
|
|
79
|
+
keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
|
|
80
|
+
});
|
|
81
|
+
taskCount++;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
cluster.on('exit', (worker, code, signal) => {
|
|
90
|
+
let allkilled = true;
|
|
91
|
+
for (const worker of Object.values(cluster.workers)) {
|
|
92
|
+
allkilled = allkilled && worker.killed;
|
|
93
|
+
}
|
|
94
|
+
if(allkilled){
|
|
95
|
+
if(USER_CONFIG.onFinish){
|
|
96
|
+
USER_CONFIG.onFinish({});
|
|
97
|
+
delete USER_CONFIG.onFinish;
|
|
98
|
+
}
|
|
99
|
+
setTimeout(()=>{
|
|
100
|
+
console.log('[Master]: all workers are done, process exit.')
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}, 1000);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}else{
|
|
106
|
+
subWorker();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
function subWorker(){
|
|
110
|
+
let worker = cluster.worker;
|
|
111
|
+
//console.log(`>[w]=${process.pid} started`);
|
|
112
|
+
process.on('message', function(message) {
|
|
113
|
+
//
|
|
114
|
+
});
|
|
115
|
+
worker.on('message', function(message) {
|
|
116
|
+
if(message === MSG_NO_TASK_FOUND){//wait for new task
|
|
117
|
+
console.log(` (Worker #${worker.id}): waiting`);
|
|
118
|
+
setTimeout(()=>{
|
|
119
|
+
worker.send(MSG_REQUEST_NEW_TASK);//request new task
|
|
120
|
+
}, 5000 + Math.random()*5000);
|
|
121
|
+
}else
|
|
122
|
+
if(message.subTask){//new task
|
|
123
|
+
let {config, subTask, startTimestamp, taskCount} = message;
|
|
124
|
+
TaskMgr.load(config);
|
|
125
|
+
//console.log('ssss', subTask)
|
|
126
|
+
if(subTask){
|
|
127
|
+
subTask.index = taskCount;//for v1 backward
|
|
128
|
+
subTask.taskCount = taskCount;
|
|
129
|
+
console.log(`[Task Start]: "${subTask.subid}"`, `pid=${process.pid}`, getDateTimeTxt());
|
|
130
|
+
runTask(config, subTask, message).then((report)=>{
|
|
131
|
+
let endTimestamp = new Date()*1;
|
|
132
|
+
let cost = startTimestamp - endTimestamp;
|
|
133
|
+
|
|
134
|
+
console.log(`[Task Finished] "${config.masterTaskId}"."${subTask.subid}".`, getDateTimeTxt(), `cost=${cost}ms`)
|
|
135
|
+
}).then(()=>{
|
|
136
|
+
worker.send(MSG_REQUEST_NEW_TASK);//request new task
|
|
137
|
+
});
|
|
138
|
+
}else{
|
|
139
|
+
console.log('Can not find: message.subTask');
|
|
140
|
+
throw 'can not find subTask!'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
worker.send(MSG_REQUEST_NEW_TASK);//request new task
|
|
145
|
+
|
|
146
|
+
};
|
|
147
|
+
function runTask(config, subTask, message){
|
|
148
|
+
return new Promise((resolve)=>{
|
|
149
|
+
let resultPromise = Promise.resolve({});
|
|
150
|
+
let resultData;
|
|
151
|
+
let errors = [];
|
|
152
|
+
helper.load(config);
|
|
153
|
+
try{
|
|
154
|
+
resultPromise = USER_CONFIG.processTask(subTask, helper);//rewrite this method in subclass
|
|
155
|
+
}catch(e){
|
|
156
|
+
errors.push({subTask, exception: 'Exception:' + e.toString()});
|
|
157
|
+
}
|
|
158
|
+
let ispromise = resultPromise instanceof Promise;
|
|
159
|
+
if(!ispromise){resultPromise = Promise.resolve(resultPromise);}//convert data to promise
|
|
160
|
+
resultPromise.then((data)=>{
|
|
161
|
+
resultData = data;
|
|
162
|
+
}).catch((ex)=>{
|
|
163
|
+
if(typeof ex !== 'string') ex = ex.toString();
|
|
164
|
+
errors.push({ex});
|
|
165
|
+
}).finally(()=>{
|
|
166
|
+
let hasError = errors.length > 0;
|
|
167
|
+
if(hasError)console.log('hasError=====', hasError, errors)
|
|
168
|
+
TaskMgr.saveTaskResult(subTask.subid, hasError, hasError ? errors : resultData);
|
|
169
|
+
TaskMgr.mvTask(subTask.subid, 'running',(hasError ? 'finished_with_errors':'finished'), (newPath)=>{
|
|
170
|
+
resolve();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
module.exports = {start};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const pathutil = require('path');
|
|
3
|
+
const makeDir = require('../utils/makedir');
|
|
4
|
+
const randomsUtil = require('../utils/randoms');
|
|
5
|
+
|
|
6
|
+
let taskCounter = 0;
|
|
7
|
+
|
|
8
|
+
let taskFolder;
|
|
9
|
+
let progressRootFolder;
|
|
10
|
+
let resultsRootFolder;
|
|
11
|
+
|
|
12
|
+
let taskFolderNew;
|
|
13
|
+
let taskFolderRunning;
|
|
14
|
+
let taskFolderFinished;;
|
|
15
|
+
let taskFolderFailed;
|
|
16
|
+
|
|
17
|
+
let taskFolderResult;
|
|
18
|
+
let taskFolderResultFailed;
|
|
19
|
+
|
|
20
|
+
let allSubTasks = {
|
|
21
|
+
new:[],
|
|
22
|
+
running:[],
|
|
23
|
+
finished:[],
|
|
24
|
+
finished_with_errors:[]
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let main_config;
|
|
28
|
+
|
|
29
|
+
const TaskMgr = {
|
|
30
|
+
load:(config)=>{
|
|
31
|
+
main_config = config;
|
|
32
|
+
return TaskMgr;
|
|
33
|
+
},
|
|
34
|
+
initFolders:()=>{
|
|
35
|
+
let {taskRootFolder, masterTaskId} = main_config;
|
|
36
|
+
taskFolder = pathutil.resolve(taskRootFolder, masterTaskId);
|
|
37
|
+
if(fs.existsSync(taskFolder)){
|
|
38
|
+
let files = fs.readdirSync(taskFolder);
|
|
39
|
+
if(files.length > 0){
|
|
40
|
+
console.log('[FATAL] taskRootFolder already exist and not empty, delete it and try again: ', taskFolder);
|
|
41
|
+
return process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
//let taskfolder = pathutil.parse(__filename).dir;
|
|
45
|
+
//let taskFolder = pathutil.resolve(taskfolder, `./tasks/${masterTaskId}`);
|
|
46
|
+
progressRootFolder = pathutil.resolve(taskFolder, './progress');
|
|
47
|
+
resultsRootFolder = pathutil.resolve(taskFolder, './results');
|
|
48
|
+
|
|
49
|
+
taskFolderNew = pathutil.resolve(progressRootFolder, './new');
|
|
50
|
+
taskFolderRunning = pathutil.resolve(progressRootFolder, './running');
|
|
51
|
+
taskFolderFinished = pathutil.resolve(progressRootFolder, './finished');
|
|
52
|
+
taskFolderFailed= pathutil.resolve(progressRootFolder, './finished_with_errors');
|
|
53
|
+
|
|
54
|
+
taskFolderResult = pathutil.resolve(resultsRootFolder, './succ');
|
|
55
|
+
taskFolderResultFailed = pathutil.resolve(resultsRootFolder, './errors');
|
|
56
|
+
//let mainRptFolder = pathutil.resolve(taskfolder, './report');
|
|
57
|
+
|
|
58
|
+
//makeDir.sync(mainRptFolder);
|
|
59
|
+
makeDir.sync(taskFolderNew);
|
|
60
|
+
makeDir.sync(taskFolderRunning);
|
|
61
|
+
makeDir.sync(taskFolderFinished);
|
|
62
|
+
makeDir.sync(taskFolderFailed);
|
|
63
|
+
makeDir.sync(taskFolderResult);
|
|
64
|
+
makeDir.sync(taskFolderResultFailed);
|
|
65
|
+
|
|
66
|
+
main_config = {
|
|
67
|
+
...main_config,
|
|
68
|
+
taskFolder,
|
|
69
|
+
progressRootFolder,
|
|
70
|
+
resultsRootFolder,
|
|
71
|
+
|
|
72
|
+
taskFolderNew,
|
|
73
|
+
taskFolderRunning,
|
|
74
|
+
taskFolderFinished,
|
|
75
|
+
taskFolderFailed,
|
|
76
|
+
taskFolderResult,
|
|
77
|
+
taskFolderResultFailed,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//for restart a failed task
|
|
81
|
+
let main_config2 = JSON.parse(JSON.stringify(main_config));
|
|
82
|
+
delete main_config2.tasks;//will load from folder 'new', so no need the early tasks data
|
|
83
|
+
fs.writeFileSync(pathutil.resolve(taskFolder, 'task_config.json'), JSON.stringify(main_config2));
|
|
84
|
+
return main_config;
|
|
85
|
+
},
|
|
86
|
+
createNewTask:(task)=>{
|
|
87
|
+
const {masterTaskId, taskFolderNew} = main_config;
|
|
88
|
+
const subid = randomsUtil.getDateTimeTxt() + Math.random();
|
|
89
|
+
|
|
90
|
+
task.masterTaskId = masterTaskId;
|
|
91
|
+
task.subid = `${masterTaskId}-${subid}`;
|
|
92
|
+
let taskfilepath = pathutil.resolve(taskFolderNew, `./${task.subid}.task.json`);
|
|
93
|
+
fs.writeFileSync(taskfilepath, JSON.stringify(task));
|
|
94
|
+
},
|
|
95
|
+
loadNewTasks:()=>{
|
|
96
|
+
const {masterTaskId, taskFolderNew} = main_config;
|
|
97
|
+
return new Promise((resolve)=>{
|
|
98
|
+
makeDir.getDirectFiles(taskFolderNew, (taskFiles)=>{
|
|
99
|
+
taskFiles.forEach((filename)=>{
|
|
100
|
+
let fpath = pathutil.resolve(taskFolderNew, filename);
|
|
101
|
+
let fcontent;
|
|
102
|
+
try{
|
|
103
|
+
fcontent = fs.readFileSync(fpath, 'utf-8');
|
|
104
|
+
}catch(e){
|
|
105
|
+
console.log('[WARN]', `file not found`, fpath)
|
|
106
|
+
}
|
|
107
|
+
if(fcontent){
|
|
108
|
+
let task = JSON.parse(fcontent);
|
|
109
|
+
allSubTasks.new.push(task);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
resolve()
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
updateNewTasks:()=>{
|
|
117
|
+
let p;
|
|
118
|
+
if(allSubTasks.new.length === 0){
|
|
119
|
+
p = TaskMgr.loadNewTasks();//load again
|
|
120
|
+
}else{
|
|
121
|
+
p = Promise.resolve();
|
|
122
|
+
}
|
|
123
|
+
return p;
|
|
124
|
+
},
|
|
125
|
+
popNewTask:()=>{
|
|
126
|
+
return new Promise((resolve)=>{
|
|
127
|
+
let newtask = allSubTasks.new.shift();
|
|
128
|
+
if(newtask){
|
|
129
|
+
TaskMgr.mvTask(newtask.subid, 'new', 'running', (newPath)=>{
|
|
130
|
+
resolve(newtask);
|
|
131
|
+
});
|
|
132
|
+
}else{
|
|
133
|
+
resolve(null);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
getTask:(type, id)=>{
|
|
138
|
+
let result;
|
|
139
|
+
let arr = allSubTasks[type];
|
|
140
|
+
for(let i=0;i<arr.length;i++){
|
|
141
|
+
let task = arr[i];
|
|
142
|
+
if(task.subid === id){
|
|
143
|
+
result = task;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
},
|
|
149
|
+
getTasks:(type)=>{
|
|
150
|
+
return allSubTasks[type];
|
|
151
|
+
},
|
|
152
|
+
getTaskFiles:(type, callback)=>{
|
|
153
|
+
let rootdir = main_config.progressRootFolder;
|
|
154
|
+
let targetFolder = pathutil.resolve(rootdir, `./${type}`);
|
|
155
|
+
makeDir.getDirectFiles(targetFolder, (files)=>{
|
|
156
|
+
callback(files);
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
mvTask: (subid, from, to, callback)=> {
|
|
160
|
+
//console.log('moooooov=======', subid, from, to)
|
|
161
|
+
let {progressRootFolder} = main_config;
|
|
162
|
+
let oldPath = pathutil.resolve(progressRootFolder, `./${from}/${subid}.task.json`);
|
|
163
|
+
let newPath = pathutil.resolve(progressRootFolder, `./${to}/${subid}.task.json`);
|
|
164
|
+
try{
|
|
165
|
+
fs.rename(oldPath, newPath, (err) => {
|
|
166
|
+
//if (err) throw err;
|
|
167
|
+
callback(newPath);
|
|
168
|
+
});
|
|
169
|
+
}catch(e){
|
|
170
|
+
callback(newPath);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
saveTaskResult:(subid, hasError, data)=>{
|
|
174
|
+
let {taskFolderResult, taskFolderResultFailed} = main_config;
|
|
175
|
+
let folder = hasError ? taskFolderResultFailed : taskFolderResult;
|
|
176
|
+
let fpath = pathutil.resolve(folder, `${subid}.json`);
|
|
177
|
+
let fcontent;
|
|
178
|
+
if(typeof data === 'undefined') {
|
|
179
|
+
fcontent = 'undefined';
|
|
180
|
+
}else{
|
|
181
|
+
fcontent = JSON.stringify(data);
|
|
182
|
+
};
|
|
183
|
+
fs.writeFileSync(fpath, fcontent);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
module.exports = TaskMgr;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const pathutil = require('path');
|
|
3
|
+
const makeDir = require('../utils/makedir');
|
|
4
|
+
|
|
5
|
+
const BUSY = 1;
|
|
6
|
+
const IDLE = 0;
|
|
7
|
+
|
|
8
|
+
let workers = {};
|
|
9
|
+
let status = {};
|
|
10
|
+
|
|
11
|
+
const mgr = {
|
|
12
|
+
addWorker:(worker)=>{
|
|
13
|
+
let id = `${worker.id}`;
|
|
14
|
+
if(workers[id] || status[id]){
|
|
15
|
+
throw 'FATAL: duplicated worker!';
|
|
16
|
+
}
|
|
17
|
+
status[id] = {isBusy: true};
|
|
18
|
+
console.log('add worker', worker.id);
|
|
19
|
+
workers[id] = worker;
|
|
20
|
+
},
|
|
21
|
+
getWorker:(id)=>{
|
|
22
|
+
return workers[`${id}`];
|
|
23
|
+
},
|
|
24
|
+
setBusy:(id, isBusy)=>{
|
|
25
|
+
|
|
26
|
+
},
|
|
27
|
+
isBusy:(id)=>{
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
module.exports = mgr;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
var fs = require('fs');
|
|
2
|
+
var pathutil = require('path');
|
|
3
|
+
const numCPUs = require('os').cpus().length;
|
|
4
|
+
|
|
5
|
+
let MSG_REQUEST_TASKID = 'request_taskid';
|
|
6
|
+
|
|
7
|
+
const TaskMgr = require('./TaskMgr');
|
|
8
|
+
const Multithread = require('./Multithread');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const fmtDigit = (n)=>{
|
|
12
|
+
return n > 9 ? "" + n: "0" + n;
|
|
13
|
+
}
|
|
14
|
+
const getDateTimeTxt = ()=>{
|
|
15
|
+
let d=new Date();
|
|
16
|
+
let YYYY=d.getFullYear()
|
|
17
|
+
let MM=fmtDigit(d.getMonth()+1);
|
|
18
|
+
let DD=fmtDigit(d.getDate());
|
|
19
|
+
let HH=fmtDigit(d.getHours());
|
|
20
|
+
let mm=fmtDigit(d.getMinutes());
|
|
21
|
+
let ss=fmtDigit(d.getSeconds());
|
|
22
|
+
return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const init = (config)=>{
|
|
26
|
+
if(typeof config === 'undefined') config = {};
|
|
27
|
+
if(!config.tasks){ throw 'Please provide tasks data!'; };
|
|
28
|
+
if(!config.processTask){ throw 'Please provide processTask function!'; };
|
|
29
|
+
if(!config.tasks){ throw 'Must assign initial tasks!'; };
|
|
30
|
+
|
|
31
|
+
if(typeof config.numberOfWorkers === 'undefined') config.numberOfWorkers = numCPUs - 1;
|
|
32
|
+
|
|
33
|
+
let {taskRootFolder, taskName} = config;
|
|
34
|
+
|
|
35
|
+
if(taskName && !taskName.match(/[a-zA-Z0-9_-]/)){ throw 'Error: taskName can only contain letters and numbers'; }
|
|
36
|
+
if(!taskName){ taskName = 'multitasks'; }
|
|
37
|
+
|
|
38
|
+
let {round, pow, random} = Math;
|
|
39
|
+
let dateTimeText = getDateTimeTxt();//moment().format('YYYY-MM-DD_HH-mm-ss');
|
|
40
|
+
let randomNum = round((random() * pow(10,8)),8).toString(36);
|
|
41
|
+
let masterTaskId = config.taskId ? config.taskId : `${taskName}${dateTimeText}--${randomNum}`;
|
|
42
|
+
|
|
43
|
+
console.log(` masterTaskId===${masterTaskId}`);
|
|
44
|
+
|
|
45
|
+
config.taskRootFolder = taskRootFolder;
|
|
46
|
+
config.masterTaskId = masterTaskId;
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
TaskMgr.load(config);
|
|
50
|
+
config = TaskMgr.initFolders(config);
|
|
51
|
+
|
|
52
|
+
config.tasks.forEach((task)=>{
|
|
53
|
+
TaskMgr.createNewTask(task);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// let tasks = TaskMgr.getTasks('new')
|
|
57
|
+
// let task = tasks.shift();
|
|
58
|
+
// console.log(tasks)
|
|
59
|
+
// TaskMgr.mvTask(task.subid, 'new', 'finished',()=>{})
|
|
60
|
+
|
|
61
|
+
//to reload is for to reStart a collapsed task
|
|
62
|
+
return new Promise((resolve)=>{
|
|
63
|
+
TaskMgr.loadNewTasks().then(()=>{
|
|
64
|
+
console.log('******start multi-thread')
|
|
65
|
+
resolve(config);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
module.exports = {
|
|
70
|
+
init
|
|
71
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var fs = require('fs');
|
|
2
|
+
var pathutil = require('path');
|
|
3
|
+
const numCPUs = require('os').cpus().length;
|
|
4
|
+
|
|
5
|
+
let MSG_REQUEST_TASKID = 'request_taskid';
|
|
6
|
+
|
|
7
|
+
const TaskMgr = require('./TaskMgr');
|
|
8
|
+
|
|
9
|
+
const init = (config, worker)=>{
|
|
10
|
+
|
|
11
|
+
};
|
|
12
|
+
module.exports = {
|
|
13
|
+
init
|
|
14
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
let fs = require('fs');
|
|
2
|
+
let pathutil = require('path');
|
|
3
|
+
|
|
4
|
+
let TaskMgr = require('./TaskMgr');
|
|
5
|
+
|
|
6
|
+
let myconfig;
|
|
7
|
+
const load = (config)=>{
|
|
8
|
+
myconfig = config;
|
|
9
|
+
};
|
|
10
|
+
const createNewTasks = (tasks)=>{
|
|
11
|
+
|
|
12
|
+
if (!Array.isArray(tasks)) tasks = [tasks];
|
|
13
|
+
TaskMgr.load(myconfig);
|
|
14
|
+
tasks.forEach((task)=>{
|
|
15
|
+
console.log('in a helper, ck', task)
|
|
16
|
+
TaskMgr.createNewTask(task);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
module.exports = {
|
|
20
|
+
load,
|
|
21
|
+
createNewTasks
|
|
22
|
+
};
|
package/workers/index.js
ADDED
package/workers/SuperConsumer.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
let fs = require('fs');
|
|
2
|
-
let pathutil = require('path');
|
|
3
|
-
|
|
4
|
-
class SuperConsumer {
|
|
5
|
-
// constructor
|
|
6
|
-
constructor(mainid, subid) {
|
|
7
|
-
this.mainid = mainid;
|
|
8
|
-
this.subid = subid;
|
|
9
|
-
this.results = [];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
setTaskTrunkInfo(trunkInfo){
|
|
13
|
-
this.trunkInfo = trunkInfo;
|
|
14
|
-
}
|
|
15
|
-
_run (mainTaskInfo, subTaskInfo, actions, results, errors, callback){
|
|
16
|
-
if(actions.length === 0) return callback({
|
|
17
|
-
finishedAt: new Date()*1,
|
|
18
|
-
results,
|
|
19
|
-
errors,
|
|
20
|
-
subTaskInfo
|
|
21
|
-
});
|
|
22
|
-
let taskProp = actions.shift();
|
|
23
|
-
|
|
24
|
-
if(this.processTask){
|
|
25
|
-
let result;
|
|
26
|
-
let hasException = false;
|
|
27
|
-
try{
|
|
28
|
-
result = this.processTask(taskProp);//rewrite this method in subclass
|
|
29
|
-
}catch(e){
|
|
30
|
-
hasException = true;
|
|
31
|
-
result = {taskProp, exception: 'Exception:' + e.toString()}
|
|
32
|
-
}
|
|
33
|
-
if(typeof result === 'undefined') result = {};
|
|
34
|
-
let ispromise = result instanceof Promise;
|
|
35
|
-
if(!ispromise){result = Promise.resolve(result);}
|
|
36
|
-
result.then((taskResult)=>{
|
|
37
|
-
if(!hasException){
|
|
38
|
-
results.push({taskProp, taskResult});
|
|
39
|
-
}else{
|
|
40
|
-
errors.push(taskResult);
|
|
41
|
-
}
|
|
42
|
-
}).catch((e)=>{
|
|
43
|
-
errors.push({taskProp, exception: 'Exception:' + e.toString()});
|
|
44
|
-
}).finally(()=>{
|
|
45
|
-
this._run(mainTaskInfo, subTaskInfo, actions, results, errors, callback);
|
|
46
|
-
});
|
|
47
|
-
}else{
|
|
48
|
-
this._run(mainTaskInfo, subTaskInfo, actions, results, errors, callback);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
run (mainTaskInfo, subTaskInfo, taskInfo, callback){
|
|
52
|
-
let results = [];
|
|
53
|
-
let errors = [];
|
|
54
|
-
let actions = subTaskInfo.actions;
|
|
55
|
-
mainTaskInfo.defination = this.trunkInfo.defination;
|
|
56
|
-
this._run(mainTaskInfo, subTaskInfo, actions, results, errors, (result)=>{
|
|
57
|
-
callback(result)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
runTasks (mainTaskInfo, subTaskInfo, message){
|
|
62
|
-
// console.log(`mainTaskInfo:`, mainTaskInfo)
|
|
63
|
-
// console.log(`subTaskInfo:`, subTaskInfo)
|
|
64
|
-
// console.log(`message:`, message)
|
|
65
|
-
|
|
66
|
-
this.mainTaskInfo = mainTaskInfo;
|
|
67
|
-
this.subTaskInfo = subTaskInfo;
|
|
68
|
-
|
|
69
|
-
return new Promise((resolve, reject)=>{
|
|
70
|
-
let mainid = mainTaskInfo.masterTaskId;
|
|
71
|
-
let subid = subTaskInfo.subid;
|
|
72
|
-
let taskfolderNew = mainTaskInfo.taskFolderNew;
|
|
73
|
-
let taskFolderResult = mainTaskInfo.taskFolderResult;
|
|
74
|
-
let taskFolderResultFailed = mainTaskInfo.taskFolderResultFailed;
|
|
75
|
-
this.mvTask(mainid, subid,'new','running', (taskFile)=>{//mark as running
|
|
76
|
-
let taskContent = fs.readFileSync(taskFile, 'utf8');
|
|
77
|
-
let taskInfo = JSON.parse(taskContent);
|
|
78
|
-
this.run(mainTaskInfo, subTaskInfo, taskInfo, (taskReport)=>{//invoke
|
|
79
|
-
let {results, errors} = taskReport;
|
|
80
|
-
let hasError = false;
|
|
81
|
-
if(errors && errors.length > 0) hasError = true;
|
|
82
|
-
let folder = !hasError ? taskFolderResult : taskFolderResultFailed;
|
|
83
|
-
let resultfile = pathutil.resolve(folder, `${subid}.json`)
|
|
84
|
-
fs.writeFileSync(resultfile, JSON.stringify(taskReport));
|
|
85
|
-
this.mvTask(mainid, subid,'running',(hasError ? 'finished_with_errors':'finished'), ()=>{//mark as finished
|
|
86
|
-
resolve({
|
|
87
|
-
mainid,
|
|
88
|
-
subid,
|
|
89
|
-
...message
|
|
90
|
-
})
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
mvTask (mainid, subid, from, to, callback) {
|
|
97
|
-
let __thisdir = this.mainTaskInfo.progressRootFolder;
|
|
98
|
-
let oldPath = pathutil.resolve(__thisdir, `./${from}/${subid}.task.json`);
|
|
99
|
-
let newPath = pathutil.resolve(__thisdir, `./${to}/${subid}.task.json`);
|
|
100
|
-
fs.rename(oldPath, newPath, (err) => {
|
|
101
|
-
if (err) throw err;
|
|
102
|
-
callback(newPath);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
module.exports = SuperConsumer;
|