能否使Node.js脚本监听同一个端口?

4
我在另一个帖子中提出了同样的问题,但回答误解了我的问题,因此我再次提问。
我有两个脚本。
 s1.js, s2.js

两者都使用 Express 框架并监听特定的端口。

我调用

node s1.js //listens to port 8080

node s2.js //listens to port 8081

要启动这两个脚本,一个监听8080端口,另一个监听8081端口。

是否可能让nodejs脚本同时监听同一个8080端口?如何在调用www.abc.com:8080时分开这两个脚本?

欢迎您的评论。

更新的问题:

我尝试编写app.js代码。

  var express = require("express");
  var app = express();

  app.use('/app1', require( './app1/index.js').app);
  app.use('/app2', require( './app2/index.js').app);

  app.listen(8081);

在路径 ./app1/index.js 中

  var express = require("express");
  var app = express();
  console.log("app1");
  app.listen(8081);

在路径 ./app2/index.js 中

  var express = require("express");
  var app = express();
  console.log("app2");
  app.listen(8081);

然后我调用了。
  node app

控制台报告错误:

app1

/Users/myname/node_modules/express/lib/application.js:111
  if (fn.handle && fn.set) app = fn;
        ^
TypeError: Cannot read property 'handle' of undefined
    at Function.app.use (/Users/zhengwang/node_modules/express/lib/application.js:111:9)
    at Object.<anonymous> (/Users/zhengwang/multi.js:27:7)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3

4
如果可能的话,他们中的哪一个会处理回应? - Lex Podgorny
你的意思是不同的Node.js脚本必须监听不同的端口,这是必须的吗? - arachide
对于 PHP,www.abc.com/s1.phpwww.abc.com/s2.php 监听相同的端口。 - arachide
1
@freakish 很棒!看起来你对这个话题很了解,为什么不把它发表为一个答案呢? - Lex Podgorny
显示剩余4条评论
3个回答

10

Cluster可以实现这一点,Express.vhost也可以。实际上,您可以同时使用两者,并且只使用一个端口。

// server.js
var cluster = require('cluster');
var express = require('express');
var app = express ();

var cpus = require('os').cpus().length - 1;

if(cluster.isMaster) {
    for(var i=0;i<cpus;i++) {
        cluster.fork ();
    }
}
else {
    app
        .use( express.vhost('www.site1.com'), require('./site1.js').app ) )
        .use( express.vhost('www.site2.com'), require('./site2.js').app ) )
        .listen(8080);
}

在site1.js和site2.js中,你应该像写任何其他node应用程序一样编写它们,除了两个小细节。将你的应用程序导出(var app = exports.app = express()),并且不要在这两个文件中监听(app.listen(8080))。你可以看到server.js已经为你做了监听,并且从它们那里需要两个“app”导出。现在,同一服务器上的所有站点都可以在同一个端口上运行,在您的服务器每个CPU上运行。我认为您的服务器本身也需要正确设置多个虚拟主机,但这超出了所提出的问题。

集群处理向正在运行的节点应用程序(server.js)之一发送请求,然后express根据使用的URL选择使用正确的应用程序(site1.js,site2.js)处理请求。令人惊讶的简单,我认为Node非常好用。


1
我没有为你编写代码,去了解一下集群和express.vhost的工作原理,以适应你的需求。 - Chris
@Chris,我真的很喜欢你在最后一段的总结;非常有帮助。 - Lonnie Best

6
好的,这里是真相:多个进程可以监听同一个端口。我知道两种情况(仅限于POSIX):
1) Forking。当主进程监听套接字并且它自己分叉时,所有子进程都会监听同一个套接字。这意味着多个进程监听同一套接字。这实际上是Node.js cluster所做的。你可能想看一下它。
2) 在两个独立的进程之间传递套接字文件描述符。是的,这可以做到。但是这需要大量的工作。基本上,两个进程通过UDS进行通信,其中一个进程将套接字文件描述符传递给另一个进程,以便另一个进程也可以监听它。但是这需要一些低级网络编程(传递套接字的FD并不像将其作为消息传递那样简单)。以下是C语言中此类实现的示例:

http://lists.canonical.org/pipermail/kragen-hacks/2002-January/000292.html

我不确定使用Node.js是否可以完成2)。

请注意,在这两种情况下,操作系统负责在进程之间进行负载平衡(决定哪个进程处理套接字上的accept和/或recv)。据我所知,大多数操作系统都做得非常好。

然而,这确实很不实用。我从未见过解决方案2)在现实生活中使用(极其复杂,存在严重的安全问题),至于解决方案1),例如Apache Web服务器广泛使用,但现在人们倾向于使用线程和/或异步系统,因此分叉变得有点过时(除非您想充分利用多个CPU核心-Node.js中没有线程)。

因此,在您的情况下,我猜最简单的解决方案是在这两个服务器前面放置一个(ha)代理,以便它将负载平衡在这两个脚本之间。

请注意,这个问题很奇怪。你说你有两个不同的服务器,但希望它们监听同一个端口。这意味着每个请求将会被其中一个处理。特别地,一半用户将由s1处理,而另一半将由s2处理。如果它们正在做同样的事情,为什么会有两个文件?如果它们在做其他事情,为什么一半的用户会得到不同的结果?看起来你试图做一些非常糟糕的事情。

1
在Windows中的内核允许多个应用程序共享一个端口,只要URL是唯一的。 - WiredPrairie
@WiredPrairie 我不明白。URL与端口共享有什么关系?URL是HTTP协议的一部分,它不在内核中。另一方面,我对Windows的魔法了解不多。 - freakish
@WiredPrairie 哦,我明白了。非常有趣,谢谢。我应该提到我只是在谈论POSIX。 - freakish
@freakish cluster 实际上也会传递文件描述符(通过 Unix 域套接字,这与 UDP 不同,顺便说一下),在 POSIX (/UNIX) 上,只要绑定的 主机名 是唯一的,就可以有多个进程监听同一个端口。 - robertklep
@robertklep 1) 这是笔误,我本想说UDS(Unix Domain Sockets) - 已经修复了。奇怪的是,我在注释中也做出了说明。用户数据报协议与此完全无关。2) 我不知道Node.js内部如何工作,但根据文档,它只是对自身进行分叉。我认为通过UDS实现自动FD共享可能会相当困难。不过我可能错了。3) 是的,我假设它们都绑定到同一个主机名。 - freakish
2
@freakish 在精细手册中有详细解释 :) - robertklep

-1

你的 /app1/index.js 和 /app2/index.js 不应该使用 listen() 方法,因为 app.js 已经在监听了,而且它们都需要导出 app 对象。

var express = require("express");
var app = exports.app = express();
console.log("app1");

app2/index.js 同上

var express = require("express");
var app = exports.app = express();
console.log("app2");

如果端口是80,想知道如何访问不同路径(app1、app2)中的index.js。 - arachide
没有理由去访问它...每个站点的所有代码都放在这两个应用程序中(app1/index.js和app2/index.js)你所有的app.get()和app.post()都在其中。 - Chris
对于 PHP,www.abc.com/app1.phpwww.abc.com/app2.php 是很正常的。这也是我希望 Node.js 脚本能够实现的。 - arachide
1
如果你只是想做这个,那么这就有点过度了。 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接