把模块放到另一个进程里调用
发布于 7 年前 作者 reesebo 3070 次浏览 来自 分享

如果我有一个模块,在它里面要进行一些消耗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进行了一下判断。然后直接把返回值通过进程消息发送给了父进程。

2 回复

为什么不用cluster模块

@laoqiren 因为有兴趣自己写

回到顶部