ExpressJS Server - 如何处理多个域
ExpressJS Server - How to handle multiple domains
我在Express上玩弄了一下,我想知道,处理链接到同一服务器的多个域的"最正确"方法是什么。
让我们假设我们有
- foo.com
- bar.net
- baz.com
这一切都指向111.222.333.444
.该机器正在运行带有Express的NodeJS。我当前的解决方案如下所示:
var express = require( 'express' ),
app = module.exports = express.createServer(),
// ... more lines ...
app.get( '/', routes.index.bind( app ) );
到目前为止,这非常简单。到目前为止,唯一的例外是在我的app.configure
电话中,我没有打电话给.use( express.static() )
。这是因为.routes.index()
方法现在看起来像这样:
var fs = require( 'fs' ),
// ... more lines ...
exports.index = function( req, res ) {
var host = /('w+'.)?(.*)'.'w+/.exec( req.header( 'host' ) ),
app = this;
switch( host[ 2 ] ) {
case 'foo':
app.use( express.static( '/var/www/foo' ) );
fs.readFile( '/var/www/foo/index.html', 'utf8', fileReadFoo );
break;
case 'bar':
app.use( express.static( '/var/www/bar' ) );
fs.readFile( '/var/www/bar/index.html', 'utf8', fileReadBar );
break;
case 'baz':
// ... lines ...
res.render( 'index', { title: 'Baz Title example' } );
break;
default:
res.send('Sorry, I do not know how to handle that domain.');
}
function fileReadFoo( err, text ) {
res.send( text );
}
function fileReadBar( err, text ) {
res.send( text );
}
};
这里发生的事情是,我分析host
条目的req.header
并解析域名。基于此,我调用.static()
方法,以便 Express 可以提供正确的静态资源等,此外,我只是简单地读取和发送索引.html文件的内容。我也尝试使用 Jade 来提供纯 HTML 文件,但 Jade 中的 include
指令只接受相对路径。
确实有效,但我不确定这是否是一种好的做法。
欢迎任何建议/帮助。
更新
我想我需要更清楚地说明这一点。我绝不是初学者。我非常了解ES和其他服务器(如NGINX)的工作原理。我正在寻找关于NodeJS/Express的正确之处的合格答案。如果使用Node/Express没有任何意义,请详细说明。如果有更好的方法可以使用Node/Express执行此操作,请解释一下。
谢谢 :-)
瓦迪姆几乎是正确的想法。您可以使用vhost
中间件配置如何响应每个域:
// `baz.com`
var app = express.createServer();
app.get( '/', routes.index );
// ...
express.createServer()
.use( express.vhost( 'foo.com', express.static( '/var/www/foo' ) ) )
.use( express.vhost( 'bar.net', express.static( '/var/www/bar' ) ) )
.use( express.vhost( 'baz.com', app ) )
.use( function( req, res ) {
res.send('Sorry, I do not know how to handle that domain.');
})
.listen( ... );
然后可以简化routes.index
,以仅处理baz.com
请求:
exports.index = function( req, res ) {
// ... lines ...
res.render( 'index', { title: 'Baz Title example' } );
};
编辑
至于比较:
switch
将首先有效地完成,并将根据host
确定如何处理所有请求 - 类似于:
express.createServer().use(function( req, res, next ) {
switch( req.host ) {
case 'foo.com': express.static( '/var/www/foo' )( req, res, next ); break;
case 'bar.net': express.static( '/var/www/bar' )( req, res, next ); break;
case 'baz.com': app.handle( req, res, next ); break;
default: res.send( ... );
}
}).listen( ... );
它允许您在启动时设置堆栈,以便任何中间件立即可用:
server.stack = [
express.vhost( 'foo.com', ... ),
express.vhost( 'bar.net', ... ),
express.vhost( 'baz.com', ... ),
[Function]
];
这些还反映了您可能遇到的 2 个可能的问题来源:
无过滤器的相同堆栈
每个Application
只有 1 个中间件堆栈,您使用的所有中间件都通过 app.use(...)
直接添加到该堆栈中。尽管在条件下添加了一些,但您仍然会得到:
app.stack = [
// ...,
app.router,
express.static( '/var/www/foo' ),
express.static( '/var/www/bar' )
];
而且条件不会改变static
中间件的响应方式 - 这是通过req.path
,而不是req.host
- 只有当它们在堆栈中开始响应时。
堆栈的状态
而且,如果在发出另一个请求之前没有添加static
中间件,那么我认为它们不会立即可用:
// GET http://foo.com/file 404
app.stack = [ app.router ]
// GET http://foo.com/ 200
app.stack = [ app.router, express.static( '/var/www/foo' ) ]
// GET http://foo.com/file 200
app.stack = [ app.router, express.static( '/var/www/foo' ) ]
这也可能意味着可以将相同的static
中间件多次添加到堆栈中:
// 3x GET http://foo.com/
app.stack = [
app.router,
express.static( '/var/www/foo' ),
express.static( '/var/www/foo' ),
express.static( '/var/www/foo' )
]
并且让它们的添加依赖于其他请求也表明可能的竞争条件:
// was `foo.com` or `bar.net` first?
app.stack = [
app.router,
express.static( ? ),
express.static( ? )
]
使用 bouncy 作为前端反向代理 - 这可以让您将完全不同的快速堆栈作为不同的服务器进程运行(每个进程都有不同的功能并分开以实现健壮性)......
然后,您可以决定如何路由到不同的端口,它适用于WebSockets。
var bouncy = require('bouncy');
bouncy(function (req, bounce) {
if (req.headers.host === 'bouncy.example.com') {
bounce(8000);
}
else if (req.headers.host === 'trampoline.example.com') {
bounce(8001)
}
}).listen(80);
由于 Express 使用 Connect,我很确定您可以使用 Connect 的虚拟主机中间件。它的操作类似于其他产品上的其他虚拟主机模块。我没有多个域来测试和向您展示正确的代码,但我认为它是这样的:
express.createServer()
.use(express.vhost('hostname1.com', require('/path/to/hostname1').app)
.use(express.vhost('hostname2.com', require('/path/to/hostname2').app)
.listen(80)
如果您到达一个Express服务器还不够的地步,那么请考虑从API使用Node.Cluster。如果这还不够,那么目前的做法是将asnyc反向代理(如Nginx)放在Express服务器前面,并将代理指向Express服务器。
在不知道意味着您必须在同一进程中运行主机的约束的情况下,很难回答这个问题,所以我将回应其他人所说的话,但希望提供更多上下文。
对node做的"最正确"的事情是在多个进程中运行主机,并在另一个进程中反向代理请求。 在同一进程中运行多个站点充满了问题,其中最重要的是一个站点崩溃,使它们全部关闭并需要重新启动整个过程。 Node的理念非常像unix,它鼓励保持程序小而独立。 如果将进程分开,则会自然地隔离应用程序。 如果您追求整体式设计,则必须编写和维护逻辑以将日志记录与不同的站点分开,并且错误处理逻辑将变得更加复杂。 毫无疑问,在其他情况下,您需要基于主机对逻辑进行分支,但您的应用程序设计将鼓励而不是阻止这种情况。
如果你反对其他技术堆栈(或者担心nginx目前在稳定分支中缺乏对websockets的支持),那么有一些用nodejs编写的可靠的反向代理,包括nodejitsu的http-proxy,它用于Joyent云上的生产PaaS堆栈和弹性(在另一个答案中提到),它的功能较少, 但我相信正在浏览器的生产中使用。
Bouncy 的作者实际上甚至建议将其作为设计 nodejs 系统时最重要的架构模式之一。 您可能还会注意到,他的回答已被一些核心节点提交者投票支持。
我使用nginx作为node.js的前端服务器。它是组织域、静态内容交付、负载控制和许多其他强大功能的最佳解决方案。绝对不需要在节点事件循环中执行此操作。这将决定应用程序的速度。
我不建议首先使用Express。事实上,即使对于单个域,使用 Node 的 http 模块,也可以轻松编写具有 Express 获得的所有功能的应用程序。
对于多个域,获得主机名后,您可以自定义路由或中间件或任何您想要调用它们的内容。只要您在任何应用程序中没有任何阻塞调用,您的单个节点进程将服务的域数量就无关紧要。如果 CPU 使用率成为瓶颈,您可以使用同一进程的集群。
如果您有内存中的会话数据,则无法进行群集。您将需要另一个进程来维护哪个实例正在管理该特定会话的状态。 这取决于您如何管理状态、状态持久性等。
总的来说,除非提供其他细节,否则答案并不简单。我提供了一个单独的答案,而不是评论,因为我相信太多的开发人员使用EXPRESS,从而限制了他们的灵活性。
有点逆流而上,我不得不说,我不明白做这样的事情有什么意义。Node.js 具有一个流程设计约束。限制 IO 对于一个 Web 应用程序来说是一项艰巨的工作,更不用说几个应用程序了。试图通过拥有多个应用程序来抽象它会使代码过于复杂,使其不可读。单个应用程序中的 bug 可能会影响所有应用程序。这是一个非常不稳定的配置。
如果你想看看你是否能做到,我想答案是肯定的。像vhost这样的东西在另一个答案中建议。 另一方面,我可能会进行某种关注点分离并限制我的应用程序。如果要将其放置在同一个服务器框中,我会执行以下操作:
- 可用内核数 -1 将是我将绑定到单个服务器的域数。
- 每个内核将持有一个节点.js进程绑定到它。它将实现覆盖单个网站的单个Web应用程序。
- 备用核心将容纳某种"路由器",要么是nginx解决方案,要么是另一个路由数据的node.js/express应用程序。
总之,不要想着做大,要想着走宽。
缺点:这是一种不同的扩展方式。你只能从一个盒子里拿出这么多果汁。当我们谈论"多盒"环境时,我不确定如何扩展这个想法。
- 可以't让我的if语句处理js中的html表单输入
- keyup事件处理程序更改焦点不适用于快速键入
- 如何使用jquery处理php循环通过元素
- angular.js没有'无法在PhoneGap中处理视图标记
- Webpack/Rect:遵循egghead.io教程,但出现错误:您可能需要一个合适的加载程序来处理此文件类型
- 提示使用服务器端事件处理程序激活JavaScript
- javascript:如何在antlr生成的Lexer中进行错误处理
- 如何编写一个具有公共标头的批处理
- 在同一个服务工作者中处理service-worker.js有任何影响吗
- 如何处理node.js节点mongodb中的连接和查询队列
- 通过命令行/批处理文件打开页面时,将javascript代码注入Google Chrome
- 如何处理10页以上的静态页眉/页脚
- 将事件处理程序绑定到任何可能的事件
- 通过ajax将坐标传递到php服务器端,并在处理后检索到javascript
- python到“;流“;字典处理
- 使用javascript进行实时图像处理
- 导入jQuery脚本获胜'我不处理html文件
- ExpressJS Server - 如何处理多个域
- (Codeigniter) jQuery数据表服务器端处理与sql server
- Angular使用Server-sent-events(EventSource)接收数据,并在两个控制器之间处理它