把模块放到另一个进程里调用
如果我有一个模块,在它里面要进行一些消耗cpu的运算,那么我把它放到程序的主进程里是不合适的,所以考虑把它放到另外一个单独的进程里执行,执行成功后将结果返回给主进程。 比如,这个模块是这样的:
module.exports = function(name){
return {
say(word){
return name + " said:'"+word+"'";
},
sleep(time){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(name + " has waken up.");
},time)
})
}
}
}
调用方式是这样的: 调用1:
var obj = proxy("./micro_svc_demo.js",true,['boy']);
obj.say('hello').then((data)=>{
console.log(data);
obj.close();
})
.catch((err)=>{
console.log(err);
obj.close();
})
调用2:
var obj = proxy("./micro_svc_demo.js",true,['boy']);
obj.sleep(1000).then((data)=>{
console.log(data);
obj.close();
})
.catch((err)=>{
console.log(err);
obj.close();
})
注意,我引入了一个proxy,这个proxy是我自己写的,它的作用是启动一个子进程,并在这个子进程中引入模块,当模块调用完毕后,我只要调用proxy的close 函数,就可以把这个进程结束掉。 下面是proxy的源码:
const cp = require("child_process");
exports.proxy = function(module_path,create_instance,init_args){
var proxy = {};
var required = require(module_path);
var resolves = {};
var rejects = {};
var cur_path = __dirname;
proxy.process = cp.fork(cur_path + "/micro_svc.js");
proxy.process.on("message",(ret)=>{
if(ret.method){
if(ret.error){
rejects[ret.method](ret.data);
}
else{
resolves[ret.method](ret.data);
}
}
else{
if(ret.error){
console.log("error",ret.data);
}
}
});
if(create_instance){
required = required.apply(required,init_args);
proxy.process.send({action:"init_instance",module_path:module_path,args:init_args});
}
else{
proxy.process.send({action:"init",module_path:module_path});
}
for(var key in required){
(function(key){
if(typeof required[key] === "function"){
proxy[key] = function(){
return new Promise((resolve,reject)=>{
var args = [];
for(var index in arguments){
args.push(arguments[index]);
}
proxy.process.send({action:"invoke",method:key,args:args});
resolves[key] = resolve;
rejects[key] = reject;
})
}
}
})(key);
}
proxy.close = function(){
if(proxy.process){
var cmd = "kill -9 " + proxy.process.pid;
cp.exec(cmd);
}
}
return proxy;
}
在proxy中,有些问题处理的不是很好,包括通过kill的方式杀掉子进程。留到以后再优化吧。 另外,还用到了一个microsvc,这是一个运行在子进程里的容器,所有要推送给子进程的模块都是通过这个容器加载的。 代码如下:
var obj = null;
process.on("message",(msg)=>{
if(msg && msg.action){
if(msg.action === "init_instance"){
if(msg.module_path){
var type_obj = require(msg.module_path);
obj = type_obj.apply(null,msg.args);
}
else{
process.send({error:true,data:"module_path not defined"});
process.exit();
}
}
else if(msg.action === "init"){
if(msg.module_path){
var type_obj = require(msg.module_path);
obj = type_obj;
}
else{
process.send({error:true,data:"module_path not defined"});
process.exit();
}
}
else if(msg.action === "invoke"){
if(!obj){
process.send({error:true,data:"invalid obj"});
process.exit();
}
else{
if(msg.method in obj){
var ret = obj[msg.method].apply(obj,msg.args);
if(ret){
if(ret.then){
return ret.then((data)=>{
process.send({error:false,data:data,method:msg.method});
}).catch((err)=>{
process.send({error:true,data:err,method:msg.method});
})
}
else{
process.send({error:false,data:ret,method:msg.method});
}
}
else{
process.send({error:false,data:null,method:msg.method});
}
}
else{
process.send({error:true,data:"method does not exist"});
process.exit();
}
}
}
}
})
由于是在子进程里调用,所以模块中所有的函数都要转换成异步调用,所以对于不返回Promise的函数,我在microsvc进行了一下判断。然后直接把返回值通过进程消息发送给了父进程。