nginx、node.js和socket.io - 它们能够很好地配合工作吗?

5

nginx是一个非常好的静态文件服务器。

它可以像这个例子一样为node.js提供服务,但功能有限。

但是nginx似乎无法代理websockets(原文链接)

我唯一找到的可能解决方案是按照这篇文章中的方法使用HAProxy作为前端 - 但该文章发布于2011年10月6日。

这肯定是一个常见的问题,但我没有找到很常见的解决方案。


解决方案

(查看https://github.com/bangkok-maco/barebone-node获取完整的解决方案和详细信息)

IP测试模式:

  • 127.0.0.12 - www.chat.nit - 公共的,在/etc/hosts和haproxy中
  • 127.0.1.12 - 内部nginx web服务器
  • 127.0.2.12 - 内部聊天服务节点.js socket.io

/etc/haproxy/haproxy.cfg:

global
 maxconn 4096
 nbproc 2
 daemon
 # user nobody
 log             127.0.0.1       local1 notice

defaults
 mode http

# listen on 127.0.0.12:80
frontend app
 bind 127.0.0.12:80
 mode tcp
 timeout client 86400000
 default_backend www_backend
 acl is_chat hdr_dom(Host) chat
 acl is_websocket path_beg /socket.io

 use_backend chat_socket_backend if is_websocket is_chat
 tcp-request inspect-delay 500ms
 tcp-request content accept if HTTP

# ngnix on 127.0.1.12:80
backend www_backend
 balance roundrobin
 option forwardfor
 mode http
 option httplog
 option httpclose
 timeout server 30000
 timeout connect 4000
 server w1 127.0.1.12:80 weight 1 maxconn 1024 check

# node (socket.io) on 127.0.2.12:80
backend chat_socket_backend
 balance roundrobin
 mode http
 option httplog
 option forwardfor
 timeout queue 5000
 timeout server 86400000
 timeout connect 86400000
 timeout check 1s
 no option httpclose
 option http-server-close
 option forceclose
 server s14 127.0.2.12:8000 weight 1 maxconn 1024 check

/etc/nginx/sites-enabled/www.chat.nit

server {
    listen   127.0.1.12:80;

    root /data/node/chat;
    index client.html;

    server_name www.chat.nit;

    # favicon.ico is in /images
    location = /favicon.ico$ { rewrite /(.*) /images/$1 last; }

    # standard includes
    location ^~ /(css|images|scripts)/ {
            try_files $uri =404;
    }

    # html page (only in root dir)
    location ~ ^/([-_a-z]+).html$ {
            try_files $uri =404;
    }

    error_page 404 /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
            root /usr/share/nginx/www;
    }
}

聊天(node.js):server.js

var app = require('http').createServer()
   , io = require('socket.io').listen(app);    
app.listen(8000,'127.0.2.12');

io.sockets.on('connection', function(socket) {
  ...
};

聊天:client.html

<head>
  <script src="/scripts/socket.io/socket.io.js"></script>
  <script>
    var socket = io.connect('http://www.chat.nit:80'); 
    ...
  </script>
</head>

注意:

  1. 将socket.io客户端js链接到scripts/目录中

    /.../scripts$ ln -s ../node_modules/socket.io/node_modules/socket.io-client/dist/ socket.io

  2. /etc/default/haproxy(与文本相反,必须设置才能正常工作)

    ENABLED=1

  3. 此版本haproxy没有记录日志。发现kvz's的写作如何通过127.0.0.1使用rsyslogd,但无法实现。

  4. 这个解决方案是有效的——肯定不是系统管理员的质量。(欢迎提出改进意见。)


实际上,HAProxy正在工作。我正在使用它来代理WebSockets,并且我对它非常满意。从我的经验来看,代理WebSockets(和其他请求)比“杀手级静态文件服务器”更重要,但实际上这取决于您的应用程序。此外,Node.js不是一个糟糕的静态文件服务器(例如使用Express框架)- 技术与nginx非常相似(异步单线程)。 - freakish
你能把你的HAProxy配置发布为一个答案吗?你知道是否有可能将HAProxy的输出分为非ws的nginx和ws/wss的node.js吗? - cc young
是的,拆分不应该是问题。毕竟,ws/wss可以通过“升级”标头识别。 - freakish
你可能会对检查 Nginx 的 tcp_proxy 感兴趣,它可以代理 websockets - Michelle Tilley
2个回答

4

2
以下是关于代理WebSockets和普通HTTP请求的HAProxy配置(旧版,仅供测试使用):

以下是我的(旧版且仅供测试目的)HAProxy配置,用于代理WebSockets和普通HTTP请求。

global
    maxconn 4096
    nbproc 2
    daemon
    user nobody

defaults
    mode http

frontend app
    bind 0.0.0.0:8000
    mode tcp
    timeout client 86400000
    default_backend www_backend
    acl is_websocket path_beg /sockets

    use_backend socket_backend if is_websocket
    tcp-request inspect-delay 500ms
    tcp-request content accept if HTTP

backend www_backend
    balance roundrobin
    option forwardfor
    mode http
    option httplog
    option httpclose
    timeout server 30000
    timeout connect 4000
    server w1 localhost:8080 weight 1 maxconn 1024 check

backend socket_backend
    balance roundrobin
    mode http
    option httplog
    option forwardfor
    timeout queue 5000
    timeout server 86400000
    timeout connect 86400000
    timeout check 1s
    no option httpclose
    option http-server-close
    option forceclose
    server s1 localhost:8081 weight 1 maxconn 1024 check

请注意,我通过查看路径 (acl is_websocket path_beg /sockets 行) 来识别请求是否为 WebSocket。这可以替换为例如以下内容:
acl is_websocket hdr(Upgrade) -i WebSocket

或者这样:
acl is_websocket hdr_beg(Host) -i ws

或者两者都可以。使用此配置将代理到nginx,应该可以直接使用。


由于旅行和天生的笨拙(笑),测试比我想象中花费了更多的时间。总之,它运作良好!非常感谢!!已将所有代码附在问题后附件中。 - cc young
@ccyoung 非常好!记得根据你的需求进行调整! - freakish
请您看一下发布的内容,如果您有任何见解,将不胜感激。 - cc young

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