5

使用最新的 Chrome 浏览器,我正在尝试使用sockjs客户端与位于haproxy. 在我的本地主机上(没有haproxy中间),这工作正常 - 客户端可以使用websocket协议连接、发送和接收消息。例如:

conn.onopen = function() {
    if (conn.readyState === SockJS.OPEN) {
        conn.send("hello server");
        console.log("msg sent");
    }
};

一旦我将它部署到服务器上HAProxy,就会发生奇怪的事情,sockjs认为连接是打开的(如conn.readyState === SockJS.OPEN控制台日志中出现“发送的消息”),但是,websocket握手只是挂起,服务器永远不会收到消息。以下是我在haproxy日志中看到的内容:

Oct 23 09:08:25 localhost.localdomain haproxy[14121]: 129.xx.xxx.105:55000 [23/Oct/2012:09:08:24.459] public www/content 777/0/0/1/778 200 375 - - ---- 3/3/0/1/0 0/0 "GET /sockjs/info HTTP/1.1"
Oct 23 09:10:54 localhost.localdomain haproxy[14121]: 129.xx.xxx.105:55015 [23/Oct/2012:09:08:25.398] public www/content 0/0/0/1/149017 101 147 - - CD-- 4/4/0/0/0 0/0 "GET /sockjs/478/kyi342s8/websocket HTTP/1.1"

请注意,第二个日志消息仅在我关闭后端服务器时出现haproxy。关机前,日志中没有错误,但是 websocket 握手没有完成,服务器也没有收到 msg。

使用 Chrome 的开发人员工具,在“网络”选项卡中,我看到以下内容:

Request URL:ws://www.mysite.com/sockjs/478/kyi342s8/websocket
Request Method:GET
Status Code:101 Switching Protocols

Request Headers
Connection:Upgrade
Host:www.mysite.com
Origin:http://www.mysite.com
Sec-WebSocket-Extensions:x-webkit-deflate-frame
Sec-WebSocket-Key:TFEIKYhlqWWBZKlXzXAuWQ==
Sec-WebSocket-Version:13
Upgrade:websocket
(Key3):00:00:00:00:00:00:00:00

Response Headers
Connection:Upgrade
Sec-WebSocket-Accept:D+s3va02KH6QTso24ywcdxcfDgM=
Upgrade:websocket
(Challenge Response):00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

并且对象的“类型”和“时间延迟”都在websocket开发人员工具的网络选项卡下显示“待定”。

最后,这是我的haproxy配置(版本 1.4.22):

global
        log 127.0.0.1   local1 info
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 4096
        chroot /usr/share/haproxy
        uid 99
        gid 99
        daemon
        #debug
        #quiet

defaults
        log             global
        mode            http
        option          httplog
        option          dontlognull
        retries         3
        option          redispatch
        maxconn         500
        timeout connect 6s

frontend public
        mode    http
        bind    *:80
        timeout client  300s
        option  http-server-close
        #option         http-pretend-keepalive
        # define ACLs
        acl host_static hdr_beg(host) -i static. data.
        acl host_www hdr_beg(host) -i www.
        acl url_static path_end .ico .txt .pdf .png .jpg .css .js .csv
        acl is_stats path_beg /haproxy/stats
        # define rules
        use_backend nginx if host_static or host_www url_static
        use_backend stats if is_stats
        default_backend www

backend nginx
        timeout server 20s
        server nginx 127.0.0.1:8484

backend stats
        stats enable
        stats uri /haproxy/stats

backend www
        timeout server 300s
        option forwardfor
        #no option httpclose
        option http-server-close
        server sockcontent 127.0.0.1:8080

有谁知道为什么会这样?是由于某些haproxy配置,甚至是服务器的某些常规网络设置(例如iptables)吗?

PS 我试过启用http-pretend-keepalivein haproxy,但它不起作用。

4

3 回答 3

2

就像@Joes 所说,这是防火墙行为不端的错误。有人说,例如 FortiGate 导致了这种情况。

更多讨论:https ://github.com/sockjs/sockjs-client/issues/94

通过 SSL 服务 SockJS 似乎解决了这个问题。

于 2012-10-30T11:07:53.957 回答
1

我已经遇到过这种情况,我不记得它是什么服务器,但它并不完全符合 WebSocket 规范。实际上,它失败了,因为它在 Connection 标头中找到了“关闭”令牌以及“升级”令牌,而规范说需要“升级”令牌并且(幸运的是)不建议拒绝其他令牌。

理论上,如果您按照您的评论使用“选项 http-pretend-keep-alive”,它应该可以工作。至少它对我有用。但也许这里有一个不同的问题。

于 2012-10-23T15:36:29.107 回答
1

最有可能的是,您的网络中有透明的防火墙,它弄乱了到端口 80 的 websocket 连接。

要验证情况是否如此:

  1. 让haproxy在不同的端口上监听并尝试它是否开始工作;
  2. 尝试在您的网络之外访问您的服务。

如果它开始工作,那么您的网络有问题。

不幸的是,SockJS 在这种情况下不会使用回退传输,因为客户端认为它已连接,但连接并未真正建立。

作为可能的解决方案:让 haproxy 监听两个端口,端口 80 用于 Web 流量,不同端口用于 SockJS 流量。这保证了透明的 HTTP 代理不会干扰你的 websocket 连接。

于 2012-10-24T20:36:25.770 回答