【问题标题】:Apache and Node.js on the Same ServerApache 和 Node.js 在同一台服务器上
【发布时间】:2012-04-07 13:32:17
【问题描述】:

我想使用 Node,因为它速度快,使用与我在客户端使用的语言相同的语言,并且根据定义它是非阻塞的。但是我雇来编写文件处理程序(保存、编辑、重命名、下载、上传文件等)的人,他想使用 apache。所以,我必须:

  1. 说服他使用 Node(他在这方面几乎没有放弃)

  2. 弄清楚如何在node或节点中上传、下载、重命名、保存等文件

  3. 我必须在同一台服务器上安装 apache 和 node。

哪种情况最有利,我该如何实施?

【问题讨论】:

    标签: apache node.js


    【解决方案1】:

    好问题!

    有许多在 Apache 上运行的用 PHP 实现的网站和免费 Web 应用程序,很多人使用它,因此您可以很容易地混搭一些东西,此外,它是一种提供静态内容的简单方法。 Node 是一个快速、强大、优雅和性感的工具,具有 V8 的原始功能和没有内置依赖项的扁平堆栈。

    我还想要 Apache 的易用性/灵活性以及 Node.JS 的繁琐和优雅,为什么我不能两者兼得

    幸运的是,使用 Apache httpd.conf 中的 ProxyPass 指令,将特定 URL 上的所有请求通过管道传输到您的 Node.JS 应用程序并不难。

    ProxyPass /node http://localhost:8000
    

    另外,请确保以下行未被注释掉,以便您获得正确的代理和子模块来重新路由 http 请求:

    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_http_module modules/mod_proxy_http.so
    

    然后在端口 8000 上运行您的 Node 应用程序!

    var http = require('http');
    http.createServer(function (req, res) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Hello Apache!\n');
    }).listen(8000, '127.0.0.1');
    

    然后您可以使用您的 url 上的 /node/ 路径访问所有 Node.JS 逻辑,网站的其余部分可以留给 Apache 来托管您现有的 PHP 页面:

    现在剩下的唯一事情就是说服您的托管公司让您使用此配置运行!!!

    【讨论】:

    • 这是一个很好的答案,只是想添加一个链接,其中包含我用来完成这项工作的代理通行证的更多信息。检查 cmets 以及。boriskuzmanovic.wordpress.com/2006/10/20/…
    • 我测试了将“ProxyPass / 127.0.0.1:8000”放入虚拟主机容器中,并且能够成功地将整个域组重定向到节点实例。我还使用“time wget ...”进行了测试,以比较直接访问节点和通过 Apache 访问节点的速度。在 30 对试验中,平均差异约为 0.56ms。直接和通过 Apache 的最低加载时间为 120 毫秒。直接加载时间最高为 154 毫秒,通过 Apache 加载时间为 164 毫秒。没有显着差异。如果我有两个 IP,我不会通过 Apache 路由,但现在我会坚持使用 Proxypass
    • 这个代理不是从 Apache 请求到 Node,同时它带走了 Node 的非阻塞特性的好处吗?
    • 嗨@Basj,我自己没有安装websockets支持的经验。话虽如此,Apache 2.4.6 似乎支持使用 mod_proxy_wstunnel 代理 websockets 流量。我看到你现在已经找到了答案,对于其他有同样问题的人,请参考:serverfault.com/questions/616370/…
    • 在基于 debian 的发行版上我应该在哪里添加这个?没有 httpd.conf 文件。
    【解决方案2】:

    这个问题更多地属于Server Fault,但我想说在大多数情况下,在 Node.js 前面运行 Apache 并不是一个好方法。

    Apache 的 ProxyPass 非常适合做很多事情(比如将基于 Tomcat 的服务作为网站的一部分公开),如果您的 Node.js 应用程序只是在做一个特定的、小角色,或者是一个可能只有有限的内部工具用户数量,那么使用它可能会更容易,这样您就可以让它工作并继续前进,但这听起来不像这里的情况。

    如果您想利用使用 Node.js 获得的性能和规模 - 特别是如果您想使用涉及维护持久连接的东西(如 Web 套接字) - 您最好同时运行 Apache 和你在其他端口上的 Node.js(例如 localhost:8080 上的 Apache,localhost:3000 上的 Node.js),然后在前面运行 nginx、Varnish 或 HA 代理之类的东西 - 并以这种方式路由流量。

    使用 varnish 或 nginx 之类的东西,您可以根据路径和/或主机路由流量。它们都使用更少的系统资源,并且比使用 Apache 做同样的事情更具可扩展性。

    【讨论】:

    • 这个答案应该有更多的赞成票。使用 nginx 代理绝对是比使用 apache 更好的方法。
    • 是的,但它是资源密集型
    • 您是否有一些数据来支持您的说法,即 nginx 会比 httpd 占用更少的资源?
    • 我不认为这很戏剧化。虽然我尽量不在回复中链接,因为链接很脆弱,但你可以通过谷歌找到一些讨论和例子——例如。 help.dreamhost.com/hc/en-us/articles/… ... Apache 是很棒的软件,但在这种情况下,它通常不是一个很好的方法。
    • 我同意 Nginx 更快,但是您需要配置和管理额外服务的开销。由于问题要求 Apache 和 Node 在同一台服务器上,因此 Nginx 似乎有点像第三个轮子。
    【解决方案3】:


    沿 apache2(v2.4.xx) server 运行 node server 的说明:

    为了将特定 URL 上的所有请求通过管道传输到您的 Node.JS 应用程序,请在 /etc/apache2/conf-available 目录中创建 CUSTOM.conf 文件,并将以下行添加到创建的文件中:

    ProxyPass /node http://localhost:8000/
    

    将 8000 更改为 node server 的首选端口号。
    使用以下命令启用自定义配置:

    $> sudo a2enconf CUSTOM
    

    CUSTOM 是您新创建的不带扩展名的文件名,然后使用以下命令启用 proxy_http

    $> sudo a2enmod proxy_http
    

    它应该同时启用proxyproxy_http 模块。您可以通过以下方式检查模块是否已启用:

    $> sudo a2query -m MODULE_NAME
    

    配置和模块启用后,需要重启apache服务器:

    $> sudo service apache2 restart
    

    现在你可以执行节点服务器了。所有对URL/node 的请求都将由节点服务器处理。

    【讨论】:

    • 像魅力一样工作! :)
    • 我收到内部服务器错误 (500),但没有任何迹象表明出了什么问题。知道是什么原因造成的,或者我在哪里可以看到一些日志吗?我是 vps 和 linux/ubuntu 的新手。
    【解决方案4】:

    在一台服务器上运行 Node 和 Apache 很简单,因为它们不会冲突。 NodeJS 只是一种在服务器端执行 JavaScript 的方式。真正的困境来自于从外部访问 Node 和 Apache。在我看来,您有两个选择:

    1. 设置 Apache 以将所有匹配的请求代理到 NodeJS,这将执行文件上传以及节点中的任何其他操作。

    2. Apache 和 Node 在不同的 IP:port 组合上(如果您的服务器有两个 IP,则可以将一个绑定到您的节点侦听器,另一个绑定到 Apache)。

    我也开始怀疑这可能不是您真正想要的。如果您的最终目标是让您在 Nodejs 中编写应用程序逻辑以及将一些“文件处理”部分卸载给承包商,那么它实际上是一种语言选择,而不是 Web 服务器。

    【讨论】:

      【解决方案5】:

      您可以使用不同的方法,例如使用 nodejs 编写反向代理服务器来代理 apache 和所有其他 nodejs 应用程序。

      首先,您需要让 apache 在端口 80 以外的其他端口上运行。例如:端口 8080

      然后你可以用nodejs写一个反向代理脚本为:

      var proxy = require('redbird')({port: 80, xfwd: false);
      
      proxy.register("mydomain.me/blog", "http://mydomain.me:8080/blog");
      proxy.register("mydomain.me", "http://mydomain.me:3000");
      

      下面的文章描述了制作这个的整个过程。

      RUN APACHE WITH NODE JS REVERSE PROXY – USING REDBIRD

      【讨论】:

        【解决方案6】:

        我将上面的答案与 certbot SSL cert 和 CORS access-control-allow-headers 结合起来并让它工作,所以我想我会分享结果。

        Apache httpd.conf 添加到文件底部:

        LoadModule proxy_module modules/mod_proxy.so
        LoadModule proxy_http_module modules/mod_proxy_http.so
        

        Apache VirtualHost 设置(PHP 的文档根位于 Apache 和带有 Certbot 的 SSL 下,而 node.js/socket.io 站点在端口 3000 上运行 - 并使用来自 Apache 的 SSL 证书) 另请注意,node.js 站点使用文件夹 /nodejs、socket.io 和 ws (websockets) 的代理:

        <IfModule mod_ssl.c>
        <VirtualHost *:443>
            ServerName www.example.com
            ServerAlias www.example.com
            DocumentRoot /var/html/www.example.com
            ErrorLog /var/html/log/error.log
            CustomLog /var/html/log/requests.log combined
            SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
            SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
            Include /etc/letsencrypt/options-ssl-apache.conf
        
            RewriteEngine On
            RewriteCond %{REQUEST_URI}  ^socket.io          [NC]
            RewriteCond %{QUERY_STRING} transport=websocket [NC]
            RewriteRule /{.*}       ws://localhost:3000/$1  [P,L]
        
            RewriteCond %{HTTP:Connection} Upgrade [NC]
            RewriteRule /(.*) ws://localhost:3000/$1 [P,L]
        
            ProxyPass /nodejs http://localhost:3000/
            ProxyPassReverse /nodejs http://localhost:3000/
        
            ProxyPass /socket.io http://localhost:3000/socket.io
            ProxyPassReverse /socket.io http://localhost:3000/socket.io
        
            ProxyPass /socket.io ws://localhost:3000/socket.io
            ProxyPassReverse /socket.io ws://localhost:3000/socket.io
        
        </VirtualHost>
        </IfModule>
        

        然后是我的 node.js 应用(app.js):

        var express = require('express');
        var app = express();
            app.use(function(req, res, next) {
                res.header("Access-Control-Allow-Origin", "*");
                res.header("Access-Control-Allow-Headers", "X-Requested-With");
                res.header("Access-Control-Allow-Headers", "Content-Type");
                res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS");
                next();
            });
        var http = require('http').Server(app);
        var io = require('socket.io')(http);
        
        http.listen({host:'0.0.0.0',port:3000});
        

        我强制使用 ip4 侦听器,但这是可选的 - 您可以替换:

        http.listen(3000);
        

        node.js 应用程序 (app.js) 代码继续:

        io.of('/nodejs').on('connection', function(socket) {
            //optional settings:
            io.set('heartbeat timeout', 3000); 
            io.set('heartbeat interval', 1000);
        
            //listener for when a user is added
            socket.on('add user', function(data) {
                 socket.join('AnyRoomName');
                 socket.broadcast.emit('user joined', data);
            });
        
            //listener for when a user leaves
            socket.on('remove user', function(data) {
                 socket.leave('AnyRoomName');
                 socket.broadcast.emit('user left', data);
            });
        
            //sample listener for any other function
            socket.on('named-event', function(data) {
                 //code....
                 socket.broadcast.emit('named-event-broadcast', data);
            });
        
            // add more listeners as needed... use different named-events...
        });
        

        最后,在客户端(创建为 nodejs.js):

        //notice the /nodejs path
        var socket = io.connect('https://www.example.com/nodejs');
        
        //listener for user joined
        socket.on('user joined', function(data) {
            // code... data shows who joined...
        });
        
        //listener for user left
        socket.on('user left', function(data) {
            // code... data shows who left...
        });
        
        // sample listener for any function:
        socket.on('named-event-broadcast', function(data) {
            // this receives the broadcast data (I use json then parse and execute code)
            console.log('data1=' + data.data1);
            console.log('data2=' + data.data2);
        });
        
        // sample send broadcast json data for user joined:
        socket.emit('user joined', {
            'userid': 'userid-value',
            'username':'username-value'
        });
        
        // sample send broadcast json data for user left 
        //(I added the following with an event listener for 'beforeunload'):
        // socket.emit('user joined', {
        //     'userid': 'userid-value',
        //     'username':'username-value'
        // });
        
        // sample send broadcast json data for any named-event:
        socket.emit('named-event', {
            'data1': 'value1',
            'data2':'value2'
        });
        

        在这个例子中,当 JS 加载时,它会向套接字发出一个“命名事件”,将 JSON 中的数据发送到 node.js/socket.io 服务器。

        使用路径/nodejs(由客户端连接)下的服务器上的io和套接字,接收数据然后将其作为广播重新发送。套接字中的任何其他用户都将使用他们的侦听器“命名事件广播”接收数据。请注意,发送者不会收到自己的广播。

        【讨论】:

          【解决方案7】:
          ProxyPass /node http://localhost:8000/     
          
          • 当我在 httpd-vhosts.conf 而不是 httpd.conf 中进行上述条目时,这对我有用
          • 我在我的环境中安装了 XAMPP,并希望通过在 8080 端口上运行的 NodeJS 应用程序(即 http://localhost/[name_of_the_node_application]

          【讨论】:

            【解决方案8】:

            我最近遇到了这个问题,我需要在基于 PHP 的 codeigniter 项目中使用 websocket 在客户端和服务器之间进行通信。

            我通过将我的端口(运行的节点应用程序)添加到 Allow incoming TCP portsAllow outgoing TCP ports 列表中解决了这个问题。

            您可以在服务器的 WHM 面板中的 Firewall Configurations 中找到这些配置。

            【讨论】:

              【解决方案9】:

              我正在寻找相同的信息。终于从@Straseus上面答案的链接中找到了答案

              http://arguments.callee.info/2010/04/20/running-apache-and-node-js-together/

              这是在 80 端口上运行 apache 网站,在 8080 端口上运行节点 js 服务并使用 .htaccess RewriteRule 的最终解决方案

              在apache网站的DocumentRoot中,添加如下内容:

              Options +FollowSymLinks -MultiViews
              
              <IfModule mod_rewrite.c>
              
              RewriteEngine on
              
              # Simple URL redirect:
              RewriteRule ^test.html$ http://arguments.callee.info:8000/test/ [P]
              
              # More complicated (the user sees only "benchmark.html" in their address bar)
              RewriteRule ^benchmark.html$ http://arguments.callee.info:8000/node?action=benchmark [P]
              
              # Redirect a whole subdirectory:
              RewriteRule ^node/(.*) http://arguments.callee.info:8000/$1 [P]
              

              对于目录级重定向,上面的链接建议使用 (.+) 规则,它需要在 'node/' 之后有一个或多个字符。我必须将它转换为 (.*),它是零个或多个,我的东西才能工作。

              非常感谢@Straseus 的链接

              【讨论】:

              • 请注意 [P] 标志需要启用 Apache 的 mod_proxy
              • 这是低效的。为什么要通过简单的ProxyPass 调用重写引擎?
              【解决方案10】:

              我假设您正在制作一个 Web 应用程序,因为您指的是 Apache 和 Node.js。快速回答 - 有没有可能 - 是的。是否推荐 - 否。 Node 捆绑了它自己的网络服务器,大多数网站在端口 80 上运行。我还假设 Nodejs 目前不支持 Apache 插件,我不确定创建虚拟主机是否是实现这一点的最佳方式。这些是像 Joyent 的好人一样维护 Nodejs 的开发人员应该回答的问题。

              最好评估 Node 的技术堆栈,而不是移植,这与大多数其他技术堆栈完全不同,这就是我喜欢它的原因,但它也涉及一些你应该提前知道的妥协。

              您的示例看起来类似于 CMS 或共享网络应用程序,并且有数百个开箱即用的应用程序可以在 Apache 上正常运行。即使你不喜欢任何现成的解决方案,你也可以用 PHP / Java / Python 编写一个 webapp,或者将它与几个现成的应用程序混合使用,它们都被设计和支持在单个 Apache 实例后面运行。

              是时候停下来想想我刚才说的话了。

              现在您已准备好决定要使用的技术堆栈。如果您的网站永远不会使用数千个需要 Apache 的现成应用程序中的任何一个,那么请选择 Node,否则您必须首先消除我之前所说的假设。

              最后,您对 techstack 的选择比任何单个组件都重要。

              我完全同意 @Straseus 的观点,即使用 node.js 文件系统 api 来处理上传和下载相对简单,但从长远来看,请更多地考虑您想要从您的网站获得什么,然后选择您的技术堆栈。

              学习 Node 的框架比学习其他框架更容易,但它不是灵丹妙药。稍加努力(这本身可能是值得的努力),您也可以学习任何其他框架。我们都互相学习,如果你作为一个小团队工作,你会比单独工作更有效率,而且你的后端技术技能也会发展得更快。因此,不要如此廉价地贬低团队其他成员的技能。

              这篇文章已经发布了大约一年,您可能已经做出了决定,但我希望我的咆哮能帮助下一个做出类似决定的人。

              感谢阅读。

              【讨论】:

                猜你喜欢
                • 2023-04-03
                • 2021-11-20
                • 2015-05-27
                • 1970-01-01
                • 1970-01-01
                • 2020-06-24
                • 1970-01-01
                • 2018-12-04
                • 2016-07-23
                相关资源
                最近更新 更多