如何在 Node.js 中从不同的应用程序注入模块

Posted

技术标签:

【中文标题】如何在 Node.js 中从不同的应用程序注入模块【英文标题】:How to inject module from different app in Node.js 【发布时间】:2017-07-18 04:20:01 【问题描述】:

我有两个节点应用程序/服务一起运行, 1.主应用 2. 第二个应用

主应用程序负责在最后显示来自不同应用程序的所有数据。现在我将第二个应用程序的一些代码放在主应用程序中,现在它可以工作了,但我希望它能够解耦。我的意思是 secnod 应用程序的代码不会在主应用程序中(通过某种方式在运行时注入它)

就像第二个服务注册到主应用程序注入它的代码一样。 它的代码只有两个模块,是否可以在nodejs中完成?

const Socket = require('socket.io-client');
const client = require("./config.json");

module.exports =  (serviceRegistry, wsSocket) =>
    var ws = null;
    var consumer = () => 
        var registration = serviceRegistry.get("tweets");
        console.log("Service: " + registration);
        //Check if service is online
        if (registration === null) 
            if (ws != null) 
                ws.close();
                ws = null;
                console.log("Closed websocket");
            
            return
        
        var clientName = `ws://localhost:$registration.port/`
        if (client.hosted) 
            clientName = `ws://$client.client/`;
        
        //Create a websocket to communicate with the client
        if (ws == null) 
            console.log("Created");
            ws = Socket(clientName, 
                reconnect: false
            );
            ws.on('connect', () => 
                console.log("second service is connected");
            );
            ws.on('tweet', function (data) 
                wsSocket.emit('tweet', data);
            );
            ws.on('disconnect', () => 
                console.log("Disconnected from blog-twitter")
            );
            ws.on('error', (err) => 
                console.log("Error connecting socket: " + err);
            );
        
    
    //Check service availability
    setInterval(consumer, 20 * 1000);

我在主模块中放置了这段代码,我想通过在运行时以某种方式注入来解耦它?示例将非常有帮助...

【问题讨论】:

为什么不将你的第二个应用导出为模块,然后你可以在你的主应用中导入它。 “通过某种方式在运行时注入”。这就是require() 你能澄清你想要这个的原因吗?正如一些答案中提到的,解耦是实现这一目标的方法。除非您有充分的理由想要这样做,否则我会说您最好使用解耦的东西 - 微服务、发布/订阅、作业队列,甚至只是记录数据以供记者使用。 【参考方案1】:

您可以从您的一个应用创建一个包,然后在另一个应用中引用该包。

https://docs.npmjs.com/getting-started/creating-node-modules

【讨论】:

谢谢,但它应该是解耦的,在这里你应该把它与require.... 这并不像你想象的那么简单...... :)【参考方案2】:

您必须使用vm 模块来实现这一点。更多技术信息在这里https://nodejs.org/api/vm.html。让我解释一下如何使用它:

    您可以使用 API vm.script 从您希望稍后运行的代码创建编译好的 js 代码。参见官方文档中的描述

创建新的 vm.Script 对象会编译代码但不会运行它。这 编译后的 vm.Script 可以在以后多次运行。重要的是要 请注意,代码未绑定到任何全局对象;相反,它是 在每次运行之前绑定,仅用于该运行。

    现在当你想插入或运行这段代码时,你可以使用script.runInContext API。

官方文档中的另一个很好的例子:

'use strict';
const vm = require('vm');

let code =
`(function(require) 

   const http = require('http');

   http.createServer( (request, response) => 
     response.writeHead(200, 'Content-Type': 'text/plain');
     response.end('Hello World\\n');
   ).listen(8124);

   console.log('Server running at http://127.0.0.1:8124/');
 )`;

 vm.runInThisContext(code)(require);

另一个直接使用js文件的例子:

var app = fs.readFileSync(__dirname + '/' + 'app.js');
vm.runInThisContext(app);

您可以将此方法用于要插入的条件代码。

【讨论】:

【参考方案3】:

有几种方法可以解耦两个应用程序。一种简单的方法是使用 pub/sub 模式(以防您不需要响应)。(现在,如果您有一个非常耦合的应用程序,它将非常除非您进行一些重构,否则很难将其解耦。)zeromq 提供了非常好的 pub/sub 实现,而且速度非常快。 例如

import zmq from "zmq";
socket.connect('tcp://127.0.0.1:5545');
socket.subscribe('sendConfirmation');

socket.on('message', function (topic, message) 
    // you can get the data from message.
    // something like:
    const msg = message.toString('ascii');
    const data = JSON.parse(msg);
    // do some actions.
    // .....

);

//don't forget to close the socket.
process.on('SIGINT', () => 
    debug("... closing the socket ....");
    socket.close();
    process.exit();
);

//-----------------------------------------
import zmq from "zmq";
socket.bind('tcp://127.0.0.1:5545');
socket.send(['sendConfirmation', someData]);

process.on('SIGINT', function() 
  socket.close();
);

这样您的模块可以有两个不同的容器(docker),只要确保打开相应的端口即可。 我不明白的是,为什么要注入 wsSocket 并且还要创建一个新的 Socket。可能我会做的只是发送 socket id,然后像这样使用它:

const _socketId = "/#" + data.socketId;     
io.sockets.connected[socketId].send("some message");

您也可以使用其他解决方案,例如 kafka 来代替 zmq,只是考虑到这样会更慢,但它会保留日志。 希望这可以让您了解如何解决您的问题。

【讨论】:

【参考方案4】:

您可以使用 npm 链接功能。

链接过程包括两个步骤:

    通过在模块的根文件夹中运行 npm link 将模块声明为全局链接 通过在目标文件夹中运行 npm link 在目标模块(应用)中安装链接模块

除非您的本地模块之一依赖于另一个本地模块,否则这非常有效。在这种情况下,链接失败,因为它找不到依赖模块。为了解决这个问题,需要将依赖模块链接到父模块,然后将父模块安装到应用程序中。

https://docs.npmjs.com/cli/link

【讨论】:

以上是关于如何在 Node.js 中从不同的应用程序注入模块的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS,Node.js,节点模块“幻影”... 注入错误,angularjs 试图加载我的指令 + 后缀

译 Node.js 中的依赖注入

node.js 的繁琐模块是不是有任何防止 sql 注入的功能?

如何在 Node.js 中从 MongoDB 返回 JSON?

如何在 Node.js 中从请求参数获取经过身份验证的用户到 socket.io

node.js如何使用回调