Nginx能否用作后端websocket服务器的反向代理?

35
我们正在开发一个Ruby on Rails应用程序,需要利用html5 websockets。目前,我们有两个独立的“服务器”:运行在nginx+passenger上的主应用程序以及使用Pratik Naik的Cramp框架(运行在Thin上)处理websocket连接的单独服务器。
理想情况下,在部署时,我们将在nginx+passenger上运行rails应用程序,并将websocket服务器代理到nginx后面,这样我们就不需要在不同的端口上运行websocket服务器。
问题是,在这种设置中,nginx似乎会过早地关闭与Thin的连接。连接成功建立到Thin服务器,然后立即使用200响应代码关闭。我们的猜测是nginx没有意识到客户端试图建立一个长时间运行的websocket流量连接。
坦白说,我对nginx配置并不是很熟悉,所以,nginx是否可以配置为websocket服务器的反向代理呢?还是我必须等待nginx提供对新的websocket握手协议的支持?假设让应用程序服务器和websocket服务器都监听端口80是必需的,那么现在是否意味着我必须让Thin在没有nginx的情况下运行在单独的服务器上?
非常感谢任何建议或建议。:)
-John

1
仍在阅读此内容的任何人都不应接受下面的当前答案。TCP代理模块运行良好,下面的答案包括有关如何设置它的链接:https://github.com/yaoweibin/nginx_tcp_proxy_module和http://www.letseehere.com/reverse-proxy-web-sockets - crockpotveggies
7个回答

26

目前你不能使用nginx来做这件事[这不再是真的],但我建议看看HAProxy。我已经用它做过这个目的。

关键是要设置长时间限制,这样socket连接才不会关闭。可以尝试设置像这样的超时时间:

timeout client  86400000 # In the frontend
timeout server  86400000 # In the backend

如果你想在同一个端口上运行Rails和Cramp应用程序,你可以使用ACL规则来检测websocket连接并使用不同的后端。因此,你的HAProxy前端配置应该类似于:

frontend all 0.0.0.0:80
  timeout client    86400000
  default_backend   rails_backend
  acl websocket hdr(Upgrade)    -i WebSocket
  use_backend   cramp_backend   if websocket

为了完整性,后端应该如下所示

backend cramp_backend
  timeout server  86400000
  server cramp1 localhost:8090 maxconn 200 check

1
太好了,谢谢!我以前没有使用过HAProxy,但一直想学习。看起来现在有个很好的理由去学了。 :) - John Reilly
2
这个答案已经不再适用(毕竟它已经三年了,这并不奇怪)。请查看@mak的回答,了解如何在nginx >= 1.3.13上进行配置。 - toxaq

12

您觉得使用我的nginx_tcp_proxy_module模块怎么样?

该模块是为Nginx设计的通用TCP代理模块。我认为这也适用于WebSocket。此外,我在开发分支中添加了tcp_ssl_module。


6
你认为,但还没有用WebSocket进行测试? - Jonas
2
@Jonas:我不知道他在发表那条评论时是否已经测试过,但我可以确认他的TCP代理现在明确支持WebSockets。 - Eli Courtwright
本文介绍如何设置、测试和使用yaoweibin的模块来托管Websocket连接:http://www.letseehere.com/reverse-proxy-web-sockets - natevw
我测试了这个模块,它运行良好。然而,您必须知道,如果您计划在标准端口80上使用node和nginx提供http内容,则不能将该模块用作其中之一将使用端口80,而另一个必须使用不同的端口号。如果您处于此情况,请使用haproxy解决方案(由@mloughran描述)。 - istvanp

11

Nginx现在支持反向代理WebSockets(版本>=1.3.13)。

# the upstream server doesn't need a prefix! 
# no need for wss:// or http:// because nginx will upgrade to http1.1 in the config below
upstream app_server {
    server localhost:3000;
}

server {
    # ...

    location / {
        proxy_pass http://app_server;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_redirect off;
    }
}

@mark,虽然这对于http很有效,但我在https方面遇到了问题。我不知何故得到了301。我已经成功地设置了带有websocket的nginx,但是在ssl上我得到了301。https://github.com/websocket-rails/websocket-rails/issues/333 这是我创建的问题。如果您能帮忙,请告诉我。谢谢。 - Pramod Solanky

7

开箱即用(官方源码)的Nginx只能与上游(=后端)建立HTTP 1.0连接,这意味着无法保持连接:Nginx将选择一个上游服务器,打开连接,代理,缓存(如果需要),然后关闭连接。就是这样。

这是需要持久连接到后端的框架不能通过Nginx工作的根本原因(没有HTTP/1.1 = 没有keepalive和Websockets)。尽管存在这种劣势,但是有一个明显的好处:Nginx可以从多个上游中选择(负载均衡)并在某些上游失败时切换到活动的上游。

编辑:自版本1.1.4以来,Nginx支持向后端发送HTTP 1.1和keepalive。支持“fastcgi”和“proxy”上游。 这里是文档


1
明白了,谢谢。那么,我现在想做的事情基本上是不可能的。也许将来nginx会支持HTTP/1.1保持连接到后端,但现在我必须想出一种替代方案。谢谢回复。 - John Reilly

5

如果有人对同样的问题感到困惑,现在nginx已经正式支持HTTP1.1上游。请查看nginx文档中的“keepalive”和“proxy_http_version 1.1”。


是的,但在1.3版本之前它不支持WebSockets。 - toxaq
确实,需要注意的是,即使发布了1.3版本,它也还没有实现。他们的路线图将提供有关Websocket实现状态的一些信息(目前计划在1.3.x中实现):http://trac.nginx.org/nginx/roadmap - Even André Fiskvik

3
Nginx新的HTTP Push模块怎么样:http://pushmodule.slact.net/。它会处理连接转换(可以这么说),一个人可能需要担心反向代理的问题。它绝对是Websockets的可行替代品,后者尚未完全混合。我知道HTTP Push模块的开发人员仍在开发完全稳定的版本,但它正在积极开发中。已经有其版本用于生产代码库中。引用作者的话,“一个有用的工具具有无聊的名字”。

谢谢,这是一个好建议。我们实际上曾经使用过这个模块来实现服务器推送,但现在我们想要支持双向通信...由于我们的应用程序只需要支持Webkit浏览器,所以我们希望现在采用纯WebSocket方法。但我很感激您的回复! :) - John Reilly

2
我使用nginx作为反向代理到一个长轮询连接的comet风格服务器上,效果非常好。确保您将proxy_send_timeout和proxy_read_timeout配置为适当值。还要确保nginx代理的后端服务器支持http 1.0,因为我认为nginx的代理模块还不支持http 1.1。
只是为了澄清一些答案中的混淆:Keepalive允许客户端重用连接以发送另一个HTTP请求。它与长轮询或保持连接开放直到发生事件无关,这正是原始问题所问的。因此,nginx的代理模块仅支持没有keepalive的HTTP 1.0就无所谓了。

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