nginx 可以用作后端 websocket 服务器的反向代理吗?

Posted

技术标签:

【中文标题】nginx 可以用作后端 websocket 服务器的反向代理吗?【英文标题】:Can nginx be used as a reverse proxy for a backend websocket server? 【发布时间】:2010-03-10 18:07:32 【问题描述】:

我们正在开发一个需要利用 html5 websockets 的 Ruby on Rails 应用程序。目前,可以说我们有两个独立的“服务器”:我们的主应用程序运行在 nginx+passenger 上,以及一个使用 Pratik Naik 的 Cramp 框架(运行在 Thin 上)的单独服务器来处理 websocket 连接。

理想情况下,当需要部署时,我们应该在 nginx+passenger 上运行 rails 应用程序,并且 websocket 服务器将代理在 nginx 后面,因此我们不需要让 websocket 服务器运行在不同的服务器上港口。

问题是,在这个设置中,nginx 似乎过早地关闭了与 Thin 的连接。与瘦服务器的连接成功建立,然后立即关闭并显示 200 响应代码。我们的猜测是 nginx 没有意识到客户端正在尝试为 websocket 流量建立一个长时间运行的连接。

诚然,我对 nginx 配置并不是那么精通,那么,是否可以将 nginx 配置为 websocket 服务器的反向代理?还是我必须等待 nginx 为新的 websocket 握手提供支持?假设要求应用服务器和 websocket 服务器同时监听端口 80,这是否意味着我现在必须在没有 nginx 的单独服务器上运行 Thin?

提前感谢您的任何意见或建议。 :)

-约翰

【问题讨论】:

仍在阅读此内容的任何人不接受以下当前答案。 TCP 代理模块运行良好,下面的答案包括如何设置它的链接:github.com/yaoweibin/nginx_tcp_proxy_module 和 letseehere.com/reverse-proxy-web-sockets 【参考方案1】:

你目前不能使用 nginx[it's not true anymore],但我建议看看 HAProxy。我已经将它用于此目的。

诀窍是设置较长的超时时间,这样套接字连接就不会关闭。比如:

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

【讨论】:

这太好了,谢谢!我以前没有用过HAProxy,但我一直很想学习。看来我现在有充分的理由这样做了。 :) 这个答案不再正确(这并不奇怪,因为它已经 3 岁了)。进一步查看@mak 的答案(目前),了解如何在 nginx >= 1.3.13 上进行配置【参考方案2】:

使用我的nginx_tcp_proxy_module module 怎么样?

这个模块是为使用 Nginx 的通用 TCP 代理而设计的。我认为它也适用于 websocket。我只是在开发分支中添加 tcp_ssl_module。

【讨论】:

认为,但还没有用 WebSocket 测试过? @Jonas:我不知道他在发表评论时是否对此进行了测试,但我可以确认他的 TCP 代理现在确实明确支持 websocket。 这篇文章解释了如何设置、测试和使用 yaoweibin 的模块来托管 Websocket 连接:letseehere.com/reverse-proxy-web-sockets 我测试了这个模块,它运行良好。但是,您必须知道,如果您计划在标准端口 80 上使用节点 nginx 提供 http 内容,那么您不能使用该模块,因为两者之一将使用端口 80 而另一个必须使用不同的端口。如果这是您的情况,请使用 haproxy 解决方案(如 @mloughran 所述)。【参考方案3】:

nginx (>= 1.3.13) now supports 反向代理 websockets。

# 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。我已经成功地在 http 上使用 websocket 设置了 nginx,但是通过 ssl 我得到了 301。github.com/websocket-rails/websocket-rails/issues/333 这是我创建的问题。让我知道您是否可以提供帮助。谢谢【参考方案4】:

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

这是需要持久连接到后端的框架无法通过 Nginx 工作的根本原因(我猜没有 HTTP/1.1 = 没有 keepalive 和 websockets)。尽管有这个缺点,但有一个明显的好处:Nginx 可以从多个上游(负载平衡)中进行选择,并在其中一些失败时故障转移到活跃的一个。

编辑:Nginx 从 1.1.4 版本开始支持 HTTP 1.1 到后端和 keepalive。支持“fastcgi”和“代理”上游。 Here it is the docs

【讨论】:

知道了,谢谢。从本质上讲,我目前正在尝试做的事情是不可能的。也许有一天 nginx 会支持后端的 HTTP/1.1 keepalives,但现在我必须想出一个替代解决方案。感谢您的回复。【参考方案5】:

对于任何想知道同样问题的人,nginx 现在正式支持上游 HTTP 1.1。有关“keepalive”和“proxy_http_version 1.1”,请参阅 nginx 文档。

【讨论】:

是的,但直到 1.3 版才支持 websockets 确实如此,应该注意的是,即使它已经发布,它也没有在 1.3 中实现。他们的路线图将提供有关 Websocket 实施状态的一些信息(目前计划用于 1.3.x):trac.nginx.org/nginx/roadmap【参考方案6】:

Nginx 与新的 HTTP 推送模块怎么样:http://pushmodule.slact.net/。它负责处理反向代理可能不得不担心的连接杂耍(可以这么说)。对于尚未完全混合的 Websocket,它无疑是一个可行的替代方案。我知道 HTTP Push 模块的开发人员仍在开发一个完全稳定的版本,但它正在积极开发中。在生产代码库中使用了它的一些版本。引用作者的话,“一个有用的工具,名字很无聊。”

【讨论】:

谢谢,这是个好建议。我们实际上使用那个模块来实现服务器推送有一段时间了,但现在我们想要支持双向通信......而且由于我们只需要为我们的应用程序支持 webkit 浏览器,我们希望与现在是一种纯粹的 websocket 方法。但我很感激你的回应! :)【参考方案7】:

我使用 nginx 反向代理到具有长轮询连接的彗星式服务器,它工作得很好。确保将 proxy_send_timeout 和 proxy_read_timeout 配置为适当的值。还要确保 nginx 代理到的后端服务器支持 http 1.0,因为我认为 nginx 的代理模块还没有 http 1.1。

只是为了澄清一些答案中的一些混淆:Keepalive 允许客户端重用连接来发送另一个 HTTP 请求。它与长时间轮询或保持连接打开没有任何关系,直到发生原始问题所问的事件。所以nginx的代理模块只支持没有keepalive的HTTP 1.0也没关系。

【讨论】:

以上是关于nginx 可以用作后端 websocket 服务器的反向代理吗?的主要内容,如果未能解决你的问题,请参考以下文章

配置 Nginx 反向代理 WebSocket

Nginx ingress pod在传入的websocket连接请求中返回404

nginx可以对websocket连接做透明压缩吗?

nginx开发读变量

WebSocket实现(nginx后端)

Rails 4 + Websocket-rails + Passenger + Nginx + 负载均衡器