资讯专栏INFORMATION COLUMN

坑系列之阿里SLB上使用Webscoket

1treeS / 1261人阅读

摘要:最终获得一个链接,里面有这样的描述如何在阿里云负载均衡上启用支持无需配置,当选用监听时,默认支持无加密版本协议协议当选择监听时,默认支持加密版本的协议协议。详细参见如何使用负载均衡性能保障型实例。

Websocket是HTML5之后的一个新事物,可以方便的实现客户端到服务端的长会话,特别适合用于客户端需要接收服务端推送的场景。例如在线客服聊天,提醒推送等等。改变了以往客户端只能通过轮询或者long poll来获取服务端状态的限制。

和HTTP协议有什么关系

首先我们来看一下Websocket协议和HTTP有什么关系呢?
本质上说,Websocket和HTTP就不是一个协议,层级不一样。但是为了兼容现有浏览器的握手规范,必须借助HTTP协议建立连接。

这是一个Websocket的握手请求

GET wss://server.example.com/ HTTP/1.1
Host: server.example.com
Pragma: no-cache
Cache-Control: no-cache
Connection: Upgrade
Upgrade: websocket
Origin: https://server.example.com
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: fFFIlFcwULSAmQacRAbS2A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

这里面有几个和一般HTTP Request不一样的地方,

Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: fFFIlFcwULSAmQacRAbS2A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

这是告诉服务端这不是一个普通的请求,而是Websocket协议。Sec-WebSocket-Key 是一个Base64 encode的值,是浏览器随机生成的,用于让服务端知道这是一个全新的socket客户端。

服务端如果开启了Socket监听,那么就会返回这样的Response

HTTP/1.1 101 Switching Protocols
Date: Fri, 09 Mar 2018 16:24:45 GMT
Connection: upgrade
upgrade: websocket
sec-websocket-accept: i/tCy92JmOXIoZwGi8ROh6CgUwk=

表示接收了请求,并且即将切换到Websocket协议,所以code是101。Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。到这里HTTP协议的任务就已经完成,之后的通信都是基于Websocket协议了。

怎么通过nginx转发Websocket的握手请求

本质上说握手请求就是一个特殊的HTTP Request,只是需要加一些上文提到的特殊内容,从Nignx官方介绍可以看到

location /wsapp/ {
    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}

只是在Request header加了两个属性,并且强制升级到HTTP 1.1,原因是HTTP 1.0不支持keep alive。如果使用HTTP 1.0发握手请求,服务端返回101以后就会直接结束这次HTTP会话了。这一点也为之后的坑埋下了伏笔。

坑从何来

自从上线了Websocket服务之后,就会经常发现socket无法建立,获得504的超时响应。

HTTP/1.1 504 Gateway Time-out
Date: Fri, 09 Mar 2018 03:34:54 GMT
Content-Type: text/html
Content-Length: 272
Connection: keep-alive

而且这一响应只有在经过SLB(负载均衡)时才有,如果直接请求到我们自己的nginx是没有问题的。但是基于对阿里的信任,还是觉得问题应该还是我们自己这儿。从code review到nginx配置,折腾了五六个小时。

最后只有自己搭建的nginx access log上寻找蛛丝马迹,一开始抓到一些响应都是499的返回,并且request_time时间都在60s上下。

[09/Mar/2018:15:04:51 +0800] 100.97.89.10 - - - 10.0.21.11  to: 10.0.20.11:8011: GET /ws/?id=168451&url=http://server.example.com/ HTTP/1.0 upstream_response_time - msec 1520579091.139 request_time 60.000 status 499 client - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36

就考虑是不是socket服务端建立连接后响应不及时,让SLB发现60s没有报文交互直接就切断请求了。

但是因为我们在前端是做了心跳的,即使服务端不响应,只要socket建立通过心跳肯定也会在60s内进行交互。不应该出现上面的场景。
之后我们把access log中socket建立成功的请求和不成功的请求分开放到一起对比,发现不成功的都是HTTP 1.0的协议。

[09/Mar/2018:15:03:51 +0800] 100.97.88.238 - - - 10.0.20.11  to: 127.0.0.1:8011: GET /ws/?id=168451&url=http://server.example.com HTTP/1.1 upstream_response_time 11.069 msec 1520579031.198 request_time 11.|
069 status 101 client - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 |
[09/Mar/2018:15:04:32 +0800] 100.97.88.254 - - - 10.0.20.11 to: 127.0.0.1:8011: GET /ws/?id=168451&url=http://server.example.com HTTP/1.0 upstream_response_time - msec 1520579072.716 request_time 36.755 s|
tatus 499 client - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36

就好像这两个请求,同一个页面发出的,但是一个成功一个失败。失败的正好就是HTTP/1.0,为什么会有两个版本的协议呢,
为了证据更加“确凿”,我们对请求进行了抓包分析,并将Sec-WebSocket-Key打印到Nginx的access log中方便trace同一个请求。

GET http://server.example.com/ws/ HTTP/1.1
Host: app.linkflowtech.com
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://server.example.com
Sec-WebSocket-Key: 8+qDYeKJGFTWKB2ov4p5TA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
[09/Mar/2018:17:07:07 +0800] 100.97.88.252 - - - 10.0.21.11  to: 10.0.20.11:8011: GET /ws/ HTTP/1.0 upstream_response_time - msec 1520586427.537 request_time 59.999 status 499 client - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 8+qDYeKJGFTWKB2ov4p5TA==  
2018-03-09 17:12:04

可以看到都是 8+qDYeKJGFTWKB2ov4p5TA== 的请求,但是在经过SLB进入nginx时候协议降级到了1.0.这叫一个酸爽,赶紧给阿里云开了工单,经过大概3~4个小时的交流。最终获得一个链接,里面有这样的描述

如何在阿里云负载均衡上启用WS/WSS支持?
无需配置,当选用HTTP监听时,默认支持无加密版本WebSocket协议(WS协议);当选择HTTPS监听时,默认支持加密版本的WebSocket协议(WSS协议)。
注意:需要将实例升级为性能保障型实例。详细参见如何使用负载均衡性能保障型实例。

这个大坑就在"注意"那一段,我们的SLB是性能共享型而不是性能保障型。看来也不是阿里云的问题,是我们的SLB档次不够高啊。知道原因后,立刻付费升级了保障型。实测一下所有问题都解决了。

虽然问题解决了,但是其实很难理解厂商的逻辑,为什么性能共享型中某些SLB节点就会降级HTTP协议版本呢,要知道1.0版本已经是一个相当落后的版本了。

在此记录一下心路历程,为了让其他使用阿里云的同学不要重蹈覆辙。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/39840.html

相关文章

  • Rancher通过Aliyun-slb服务对接阿里SLB教程

    摘要:本文将详尽演示如何通过服务对接阿里云。概要阿里云负载均衡是将访问流量根据转发策略分发到后端多台云服务器,简称的流量分发控制服务。 阿里云负载均衡(Server Load Balancer)是将访问流量根据转发策略分发到后端多台云服务器(ECS)的流量分发控制服务。 本文将详尽演示Rancher如何通过Aliyun-slb服务对接阿里云SLB。 概要 阿里云负载均衡(Server Loa...

    baihe 评论0 收藏0
  • Rancher通过Aliyun-slb服务对接阿里SLB教程

    摘要:本文将详尽演示如何通过服务对接阿里云。概要阿里云负载均衡是将访问流量根据转发策略分发到后端多台云服务器,简称的流量分发控制服务。 阿里云负载均衡(Server Load Balancer)是将访问流量根据转发策略分发到后端多台云服务器(ECS)的流量分发控制服务。 本文将详尽演示Rancher如何通过Aliyun-slb服务对接阿里云SLB。 概要 阿里云负载均衡(Server Loa...

    Dean 评论0 收藏0
  • 阿里SLB负载均衡公网类型和私网类型区别

    摘要:对于负载均衡的公网和私网区别官方文档什么是负载均衡实例中已经做了详细解读。私网负载均衡实例私网类型的负载均衡提供的是私网,私网类型的负载均衡实例只能在阿里云内部使用,可以转发的请求只能来自具有负载均衡的私网访问权限的客户端。SLB负载均衡可以为多台云服务器提供流量分发服务,阿里云的SLB负载均衡实例分为公网类型和私网类型两种,那么二者之间有什么区别?云吞铺子来说说: 公网SLB和私网SLB区...

    Binguner 评论0 收藏0
  • 详解SLB、EIP、NAT网关间区别, 合理选择云公网入口

    摘要:注意负载均衡仅提供被动访问公网的能力,即后端只能在收到通过负载均衡转发来的公网的请求时,才能访问公网回应该请求,不具备功能。公网带宽购买时配置弹性公网网关均可以实现的双向公网访问访问或被访问,但没有流量分发和负载均衡的能力。 摘要: 概述 阿里云的公网入口产品共有三个,SLB、EIP、NAT网关,这几个产品都可以作为云上资源的公网入口,他们之间有何区别,又分别应该在什么场景下使用呢? ...

    hqman 评论0 收藏0
  • 详解SLB、EIP、NAT网关间区别, 合理选择云公网入口

    摘要:注意负载均衡仅提供被动访问公网的能力,即后端只能在收到通过负载均衡转发来的公网的请求时,才能访问公网回应该请求,不具备功能。公网带宽购买时配置弹性公网网关均可以实现的双向公网访问访问或被访问,但没有流量分发和负载均衡的能力。 摘要: 概述 阿里云的公网入口产品共有三个,SLB、EIP、NAT网关,这几个产品都可以作为云上资源的公网入口,他们之间有何区别,又分别应该在什么场景下使用呢? ...

    邹强 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<