Apache 和 Node.js 位于同一服务器上

Apache and Node.js on the Same Server

提问人:Matt 提问时间:3/23/2012 最后编辑:Michael IrigoyenMatt 更新时间:4/1/2021 访问量:293980

问:

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

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

  2. 了解如何在 node 或

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

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

Apache 节点 .js

评论


答:

-5赞 RHT 4/21/2013 #1

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

与其评估端口,不如评估 Node 的技术堆栈,它与大多数其他技术堆栈完全不同,这就是我喜欢它的原因,但它也涉及一些您应该提前了解的妥协。

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

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

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

最后,您对技术堆栈的选择比任何单个组件都重要得多。

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

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

这篇文章大约有一年的历史了,很可能你已经决定了,但我希望我的咆哮能帮助下一个正在经历类似决定的人。

感谢您的阅读。

17赞 Yarek T 4/21/2013 #2

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

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

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

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

769赞 Steven de Salas 9/4/2013 #3

好问题!

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

我也想要Apache的易用性/灵活性,但Node.JS的咕噜声和优雅,为什么我不能两者兼而有之?

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

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.JS逻辑,网站的其余部分可以留给Apache来托管您现有的PHP页面:/node/

enter image description here

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

评论

6赞 Alex Muro 10/16/2013
这是一个很好的答案,只是想添加一个链接,其中包含我用来完成这项工作的代理通行证的更多信息。也检查评论。boriskuzmanovic.wordpress.com/2006/10/20/......
12赞 kaan_a 3/19/2014
我测试了将“ProxyPass / 127.0.0.1:8000”放入虚拟主机容器中,并能够成功地将整个域组重定向到节点实例。我还用“time wget...” 进行了测试。将直接访问节点的速度与通过 Apache 访问节点的速度进行比较。在30对试验中,平均差异约为0.56ms。直接和通过 Apache 的最低加载时间为 120 毫秒。直接加载时间最高为 154 毫秒,通过 Apache 加载时间为 164 毫秒。没有显着差异。如果我有两个IP的奢侈,我不会通过Apache路由,但现在我会坚持使用Proxypass
5赞 html_programmer 9/17/2014
这难道不是从Apache到Node的代理请求,同时剥夺了Node的非阻塞特性的好处吗?
2赞 Steven de Salas 12/18/2014
嗨,@Basj,我自己没有安装对 websocket 的支持的经验。话虽如此,Apache 2.4.6 似乎支持使用 .我看到你现在已经找到了你的答案,对于其他有同样问题的人,请参阅:serverfault.com/questions/616370/......mod_proxy_wstunnel
4赞 santi 7/27/2015
在基于 debian 的发行版上,我应该在哪里添加它?没有 httpd.conf 文件。
-1赞 pd1980 10/12/2013 #4

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

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

这是在端口 80 上运行 apache 网站,在端口 8080 上运行 node 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

评论

3赞 Simon East 6/20/2014
请注意,[P] 标志需要启用 Apache。mod_proxy
0赞 Michael Irigoyen 10/12/2015
这是低效的。为什么要在简单的 ?ProxyPass
85赞 Iain Collins 10/7/2014 #5

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

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

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

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

评论

0赞 The Oracle 5/21/2018
是的,但它是资源密集型的
1赞 RedShift 10/4/2018
您是否有一些数字来支持您的说法,即 nginx 的资源密集度低于 httpd?
0赞 Iain Collins 10/5/2018
我不认为这很戏剧化。虽然我尽量不在回复中链接出来,因为链接很脆弱,但你可以通过谷歌找到一些讨论和例子——例如 help.dreamhost.com/hc/en-us/articles/......Apache是很棒的软件,但在这样的环境中,它通常不是一个好方法。
0赞 Pierre 4/29/2019
这个答案听起来不错,但是如何通过 httpS 访问 Node.js,因为它已经被 Apache 采用?
2赞 Steven de Salas 6/3/2020
我同意 Nginx 更快,但这样你就需要配置和管理额外服务的开销。由于问题要求Apache和Node在同一台服务器上,因此Nginx似乎有点像第三轮。
42赞 krmld 4/3/2016 #6


运行说明:

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

ProxyPass /node http://localhost:8000/

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

$> sudo a2enconf CUSTOM

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

$> sudo a2enmod proxy_http

它应该同时启用 AND 模块。您可以通过以下命令检查模块是否启用:proxyproxy_http

$> sudo a2query -m MODULE_NAME

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

$> sudo service apache2 restart

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

评论

0赞 Kees Koenen 6/13/2019
像魅力一样工作!:)
0赞 DFSFOT 6/5/2021
我收到内部服务器错误 (500),没有任何迹象表明出了什么问题。知道是什么原因导致的,或者我在哪里可以看到一些日志吗?我是 vps 和 linux/ubuntu 的新手。
0赞 Mike Lowery 5/7/2023
如果第一个参数以尾随 / 结尾,则第二个参数也应以尾随 / 结尾,反之亦然。否则,对后端的最终请求可能会遗漏一些所需的斜杠,并且无法提供预期的结果。
11赞 wathmal 6/15/2016 #7

您可以使用不同的方法,例如使用 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");

以下文章描述了制作此操作的整个过程。

使用 NODE JS 反向代理运行 APACHE – 使用 REDBIRD

3赞 rahul shukla 2/8/2018 #8
ProxyPass /node http://localhost:8000/     
  • 当我在 httpd-vhosts.conf 而不是 httpd.conf 中输入上述条目时,这对我有用
  • 我在我的环境中安装了 XAMPP,并希望在端口 80 上使用 NodeJS applicatin 在 8080 端口上运行,即 http://localhost/[name_of_the_node_application]
0赞 Paritosh Pandey 1/23/2020 #9

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

我通过将我的端口(运行在节点上的应用程序)添加到&列表来解决此问题。Allow incoming TCP portsAllow outgoing TCP ports

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

9赞 Dr. Aaron Dishno 5/29/2020 #10

我将上面的答案与 certbot SSL 证书和 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 和 SSL 下,带有 Certbot,而 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);

节点 .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 和套接字(由客户端连接),接收数据,然后将其作为广播重新发送。套接字中的任何其他用户都将通过其侦听器“named-event-broadcast”接收数据。请注意,发送方不会收到自己的广播。