【问题标题】:Haproxy socket.io websocket proxy always falls back to long pollingHaproxy socket.io websocket 代理总是回退到长轮询
【发布时间】:2012-05-25 06:39:19
【问题描述】:

嘿,我一直在尝试从 socket.io 获取 Haproxy 代理 websocket 连接

我已经阅读了几乎所有可以在 google 上找到的内容,并尝试了 haproxy.cfg 的无数变体,但无论我尝试什么,socket.io 总是退回到长轮询。

值得一提的是,如果我将连接直接路由到我正在使用的套接字服务器,则 ws 连接可以完美运行。

所以我使用 socket.io 客户端和 tornado tornado2 websocket 服务器。我当前的 haproxy.cfg 取自这里 haproxy example conf

defaults
mode    http

frontend all
bind 0.0.0.0:80
mode tcp

maxconn 200000
timeout client 86400000
default_backend www_backend

# Any URL beginning with socket.io will be flagged as 'is_websocket'
acl is_websocket path_beg /socket.io
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_websocket hdr_beg(Host) -i ws

# The connection to use if 'is_websocket' is flagged
use_backend socket_backend_http if is_websocket

tcp-request inspect-delay 500ms
tcp-request content accept if HTTP   

backend www_backend
option httplog
option httpclose
balance roundrobin
option forwardfor
timeout server 30000
timeout connect 4000
server nginx localhost:81 weight 1 maxconn 1024 check

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

websocket 请求被正确路由到 socket_backend_http 但浏览器控制台总是显示以下错误

Unexpected response code: 400

然后 socket.io 在短时间消息按预期出现后回退到长轮询。 我已经使用最新版本的 chrome、safari 和 firefox 进行了测试,结果都相同

我看到很多人说他们有这个工作让我绝望!我将永远感激任何设法解决这个问题的人。

再次澄清一下 haproxy 绑定到 80 端口,nginx 在 81 端口上运行,套接字服务器在 3012 端口上运行。如果有人觉得查看套接字服务器很有用,请发表评论并修改贴上代码

提前致谢

编辑 当前的 haproxy.cfg 实际上导致在 tornado 服务器中引发此错误

Traceback (most recent call last):
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-     packages/tornado/ioloop.py", line 399, in _run_callback
callback()
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/iostream.py", line 304, in wrapper
callback(*args)
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/httpserver.py", line 250, in _on_headers
self.request_callback(self._request)
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/web.py", line 1362, in __call__
handler._execute(transforms, *args, **kwargs)
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/web.py", line 992, in _execute
self._handle_request_exception(e)
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/web.py", line 1032, in _handle_request_exception
self.send_error(500, exc_info=sys.exc_info())
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/web.py", line 688, in send_error
self.finish()
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/web.py", line 669, in finish
self.request.finish()
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/httpserver.py", line 422, in finish
self.connection.finish()
File "/home/mike/.virtual-envs/watchdog-app/local/lib/python2.7/site-packages/tornado/httpserver.py", line 183, in finish
assert self._request, "Request closed"

更新

好的,还有一些进一步的发展。我已经设法通过使用 stunnel 的稍微不同的设置来完成这项工作。所以 nginx 现在将 http 请求重新路由到 https 并被 stunnel 终止,然后将请求转发到 haproxy 接收的端口 8443 上。套接字握手完成,一切都按预期工作,这很好,但我喜欢有人能够启发我为什么这适用于 ssl 而不是 http !!!

【问题讨论】:

    标签: python websocket socket.io tornado haproxy


    【解决方案1】:

    做了一些快速的研究——个人并不熟悉,但我一直在使用 SockJS 并注意到了同样的行为。我至少发现 Nginx 与 websockets 不兼容,并且 HAProxy 需要努力才能开始工作,就像你发现的那样。 haproxy and socket.io not fully working

    示例配置与来自以下来源 http://book.mixu.net/ch13.html 的配置非常相似,看起来与您的配置有 99% 的相似度。

    https://github.com/mixu/sioconfig/blob/master/single.haproxy.cfg

    我发现的另外两个花絮是 - 来自HAProxy + WebSocket Disconnection

    • 确保您的 HAProxy 为 1.4 或更高版本
    • 确保您的 socket.io 是 0.6.8 或更高版本

    【讨论】:

    • 嘿,感谢您的回答。我已经阅读了您在回答中建议的所有内容,但似乎都不起作用:(我看到很多相反的说法说由于内容长度标头设置不正确,这实际上不起作用。我认为需要 8 个字节,这就是为什么我看到错误的请求被退回的原因。我一直在毫无意义地试图让 haproxy irc 频道的一些人发表一些看法,但它是一个鬼城!
    【解决方案2】:

    我不能谈论 Socket.io,但是 SockJS 经过测试并且在最近的 haproxy(例如 1.4.16)之后运行良好。查看示例配置:

    我还没有想出在 Nginx 后面运行 SockJS 的方法。

    【讨论】:

    • 嘿,谢谢你的回答,我对 SockJS 的了解不多,而且似乎应该可以使用 socket.io。至于 nginx 背后的运行和套接字服务器,我们将不得不等到 nginx 的 1.3 版本,在那里他们将引入对 websocket 握手所需的 http 1.1 协议的支持。
    【解决方案3】:

    为了明确一点,WebSocket 中没有 HTTP 内容,因此不需要任何 content-length 标头。

    另外,您的配置存在错误。前端处于 TCP 模式,因此 ACL 仅在请求来得足够快时才会匹配。事实上,有时它可能部分靠运气。请将您的前端设置为“模式 http”以解决此问题, 并删除您变得无用的“tcp-inspect”规则。顺便说一句,您的“http-backend”也缺少 HTTP 模式。

    我猜你没有检查日志,否则你可能会注意到它们在 TCP 中并不是真正可利用的:-/

    【讨论】:

      【解决方案4】:

      我知道这是一个老问题,但是:

      Haproxy 无法代理 websocket(截至 2013 年 6 月)。 我很确定错误的原因是因为它将它视为正常的http连接并超时连接。你可以让超时持续很长时间,但这感觉很脏。

      您的选择是:

      • Nginx 1.4 版增加了对代理 websockets 的支持见http://nginx.org/
      • 使用 Nodejitsu 的 websocket 代理 http://blog.nodejitsu.com/http-proxy-intro(当我们放置 > 5k 个并发连接时,发现它会泄漏内存并崩溃)。
      • 客户端平衡例如使用server<n>.x.com
      • 使用amazon的route53做DNS roundrobin(我说amazon是因为它支持健康检查,但是如果你不需要HA,普通的DNS就可以了)
      • 恐慌……

      要记住的一点是,您还需要进行粘性会话或使用集中式会话存储。 SockJS 比 socket.io 更容易做到这一点,因为它会在每个会话的 url 中放置一个随机字符串,因此您可以简单地基于 url 进行平衡器。您可以使用集中式存储(例如 redis)与 Socket.io 进行会话,我觉得这很笨拙。

      Nginx 和 nodejitsu 选项都可以为您终止 SSL。

      【讨论】:

        猜你喜欢
        • 2015-02-06
        • 2017-11-30
        • 1970-01-01
        • 2014-12-23
        • 2012-02-06
        • 2015-11-27
        • 2015-09-14
        • 2013-07-16
        • 1970-01-01
        相关资源
        最近更新 更多