【问题标题】:How do I host multiple Node.js sites on the same IP/server with different domains?如何在具有不同域的同一 IP/服务器上托管多个 Node.js 站点?
【发布时间】:2013-10-15 18:46:21
【问题描述】:

我有一个绑定了单个 IP 的 linux 服务器。我想在这个 IP 上的这个服务器上托管多个 Node.js 站点,每个(显然)都有一个唯一的域或子域。我希望它们都在 80 端口上。

我有哪些选择?

一个明显的解决方案似乎是让 node.js Web 应用程序为所有域提供服务,该应用程序充当代理并传递到在唯一端口上运行的其他 node.js 应用程序。

【问题讨论】:

  • 我在上游使用 nginx,基于名称的虚拟主机。作为奖励,nginx 可以配置为提供静态文件、进行永久重定向等。

标签: node.js


【解决方案1】:

选择以下之一:

  • 使用其他服务器 (like nginx) 作为反向代理。
  • 使用node-http-proxy 作为反向代理。
  • 如果可以从相同的 Connect/Express 代码库和 node.js 实例为每个域提供服务,请使用 vhost middleware

【讨论】:

  • 这是我在其他地方读到的一个非常好的和简短的选项列表。您是否碰巧知道每个解决方案在添加新域时需要重新启动哪些进程?对于 1) 无。对于 2) 只有 node-http-proxy。对于 3) 所有站点的整个线程都需要重新启动。这是正确的吗?
  • @Flion:您可以编写基于节点的代理,这样您就可以重新加载域配置而无需重新启动进程。这实际上取决于您的应用的确切要求。
  • 不是问的。
【解决方案2】:

Diet.js 有非常好的和简单的方法来使用同一个服务器实例托管多个域。您只需为每个域调用一个新的server()

一个简单的例子

// Require diet
var server = require('diet');

// Main domain
var app = server()
app.listen('http://example.com/')
app.get('/', function($){
    $.end('hello world ')
})

// Sub domain
var sub = server()
sub.listen('http://subdomain.example.com/')
sub.get('/', function($){
    $.end('hello world at sub domain!')
})

// Other domain
var other = server()
other.listen('http://other.com/')
other.get('/', function($){
    $.end('hello world at other domain')
})

分离您的应用程序

如果您想为您的应用程序设置不同的文件夹,那么您可以采用如下的文件夹结构:

/server
   /yourApp
       /node_modules
       index.js
   /yourOtherApp
       /node_modules
       index.js
   /node_modules
   index.js

/server/index.js 中,您需要每个应用程序的文件夹:

require('./yourApp')
require('./yourOtherApp')

/server/yourApp/index.js 中,您将设置您的第一个域,例如:

// Require diet
var server = require('diet')

// Create app
var app = server()
app.listen('http://example.com/')
app.get('/', function($){
    $.end('hello world ')
})

/server/yourOtherApp/index.js 中,您将设置您的第二个域,例如:

// Require diet
var server = require('diet')

// Create app
var app = server()
app.listen('http://other.com/')
app.get('/', function($){
    $.end('hello world at other.com ')
});

阅读更多:

【讨论】:

  • 这条评论解决了我所有的问题 :) ... 即使在 2016 年
  • 这个会自动重新加载吗?我开始使用 PM2,但它不允许我托管多个域和反向代理
  • 这个是取代 express 还是和它一起工作?我的猜测也是。但在我创建一个科学怪人之前,我想确定一下。
  • 仅供参考 截至 2019 年 7 月 -dietjs.com 已不再存在,并且在大约 2 年内没有更新 github 代码库。
【解决方案3】:

嗯...为什么您认为 nodejs 应该充当代理。我会建议运行几个节点应用程序来监听不同的端口。然后使用 nginx 将请求转发到正确的端口。如果使用单个 nodejs,您还将遇到单点故障。如果该应用程序崩溃,那么所有网站都会崩溃。

【讨论】:

    【解决方案4】:

    我有一个在网站上使用的 API,下面是我的配置。我也有 SSL 和 GZIP,如果有人需要,请评论我。

    var http = require('http'),
        httpProxy = require('http-proxy');
    
    var proxy_web = new httpProxy.createProxyServer({
            target: {
                host: 'localhost',
                port: 8080
            }
        });
    
        var proxy_api = new httpProxy.createProxyServer({
            target: {
                host: 'localhost',
                port: 8081
            }
        });
    
        http.createServer(function(req, res) {
            if (req.headers.host === 'http://www.domain.com') {
                proxy_web.proxyRequest(req, res);
                proxy_web.on('error', function(err, req, res) {
                    if (err) console.log(err);
                    res.writeHead(500);
                    res.end('Oops, something went very wrong...');
                });
            } else if (req.headers.host === 'http://api.domain.com') {
                proxy_api.proxyRequest(req, res);
                proxy_api.on('error', function(err, req, res) {
                    if (err) console.log(err);
                    res.writeHead(500);
                    res.end('Oops, something went very wrong...');
                });
            }
        }).listen(80);
    

    【讨论】:

      【解决方案5】:

      使用 nginx 作为反向代理。

      http://www.nginxtips.com/how-to-setup-nginx-as-proxy-for-nodejs/

      Nginx 以缓存、静态文件处理、ssl 和负载平衡的形式为您的应用程序带来了诸多好处。

      【讨论】:

        【解决方案6】:

        如果您使用的是连接/快速服务器,您可以看到vhost 中间件。它将允许多个域(子域)用于服务器地址。

        您可以按照here 给出的示例进行操作,看起来完全符合您的需要。

        【讨论】:

          【解决方案7】:

          以下是使用普通 Node.js 的方法:

          const http = require('http')
          const url = require('url')
          const port = 5555
          const sites = {
            exampleSite1: 544,
            exampleSite2: 543
          }
          
          const proxy = http.createServer( (req, res) => {
            const { pathname:path } = url.parse(req.url)
            const { method, headers } = req
            const hostname = headers.host.split(':')[0].replace('www.', '')
            if (!sites.hasOwnProperty(hostname)) throw new Error(`invalid hostname ${hostname}`)
          
            const proxiedRequest = http.request({
              hostname,
              path,
              port: sites[hostname],
              method,
              headers 
            })
          
            proxiedRequest.on('response', remoteRes => {
              res.writeHead(remoteRes.statusCode, remoteRes.headers)  
              remoteRes.pipe(res)
            })
            proxiedRequest.on('error', () => {
              res.writeHead(500)
              res.end()
            })
          
            req.pipe(proxiedRequest)
          })
          
          proxy.listen(port, () => {
            console.log(`reverse proxy listening on port ${port}`)
          })
          

          很简单吧?

          【讨论】:

            【解决方案8】:

            首先安装foreverbouncy

            然后编写一个启动脚本。在此脚本中,向 iptables 防火墙实用程序添加一条规则,告诉它将端口 80 上的流量转发到端口 8000(或您选择的任何其他端口)。在我的示例中,8000 是我运行 bouncy 的位置

            sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
            

            永远使用,让我们告诉脚本在端口 8000 上运行 bouncy

            forever start --spinSleepTime 10000 /path/to/bouncy /path/to/bouncy/routes.json 8000
            

            routes.json 类似于

            {
                “subdomain1.domain.com" : 5000,
                “subdomain2.domain.com" : 5001,
                “subdomain3.domain.com" : 5002
            }
            

            NodeJS application1、application2 和 application3 分别运行在 5000、5001 和 5002 端口。

            我在我的案例中使用的脚本可以在 here 找到,您可能需要进行一些更改以适应您的环境。

            我也写了更多的细节,你可以找到它here

            【讨论】:

            • 你应该检查 PM2
            • 好的,这很好,但我怎样才能强制 http-> https 有弹性??
            【解决方案9】:

            这是我最简单的演示项目,没有任何中间件或代理。
            只需要几个代码,就足够了。

            https://github.com/hitokun-s/node-express-multiapp-demo

            通过这种结构,您可以轻松地独立设置和维护每个应用程序。
            我希望这对你有帮助。

            【讨论】:

              【解决方案10】:

              从字面上看,当你拿到request和response对象的时候,就可以通过“request.headers.host”来获取域名...(不是IP地址,实际上是域名)。

              【讨论】:

                【解决方案11】:

                基于@Michaaatje 和@papiro,一个非常简单的方法:

                假设您有一些典型的页面,例如...

                var app = express()
                app.use(sess)
                app.use(passport.initialize())
                app.use(passport.session())
                app.use('/static', express.static('static'))
                
                app.get('/', ensureLoggedIn("/loginpage"), function(req, res, next) {
                    ...
                })
                
                app.get('/sales', ensureLoggedIn("/loginpage"), function(req, res, next) {
                    ...
                })
                
                app.get('/about', ensureLoggedIn("/loginpage"), function(req, res, next) {
                    ...
                })
                
                app.post('/order', ensureLoggedIn("/loginpage"), urlencodedParser, (req, res) => {
                    ...
                })
                

                ..等等。

                假设主域是“abc.test.com”

                但是您有一个“备用”域(可能是针对客户的),即“customers.test.com”。

                只需添加这个 ...

                var app = express()
                app.use(sess)
                app.use(passport.initialize())
                app.use(passport.session())
                app.use('/static', express.static('static'))
                
                app.use((req, res, next) => {
                    req.isCustomer = false
                    if (req.headers.host == "customers.test.com") {
                        req.isCustomer = true
                    }
                    next();
                })
                

                然后就这么简单......

                app.get('/', ensureLoggedIn("/loginpage"), function(req, res, next) {
                    if (req.isCustomer) {
                        .. special page or whatever ..
                        return
                    }
                    ...
                })
                
                app.get('/sales', ensureLoggedIn("/loginpage"), function(req, res, next) {
                    if (req.isCustomer) {
                        res.redirect('/') .. for example
                        return
                    }
                    ...
                })
                
                app.get('/about', ensureLoggedIn("/loginpage"), function(req, res, next) {
                    if (req.isCustomer) { ... }
                    ...
                })
                
                app.post('/order', ensureLoggedIn("/loginpage"), urlencodedParser, (req, res) => {
                    if (req.isCustomer) { ... }
                    ...
                })
                

                感谢@Michaaatje 和@papiro。

                【讨论】:

                  【解决方案12】:

                  这个来自数字海洋的guide 是一个很好的方式。它使用 pm2 模块来守护您的应用程序(将它们作为服务运行)。不需要像 Forever 这样的额外模块,因为它会在你的应用程序崩溃时自动重启。它具有许多功能,可帮助您监控服务器上运行的各种应用程序。真是太棒了!

                  【讨论】:

                  • PM2 是否监听 80 端口?
                  • @Guy 在指南的底部是关于使用 nginx 设置反向代理的部分,它将 pm2 暴露到端口 80
                  • 我在许多项目中使用 PM2。这是一个很棒的产品。我只是误解了您的回答,因为这里的原始问题有点不同,我认为您是在暗示它满足了这一需求。谢谢。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-12-10
                  • 2017-03-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-08-05
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多