本篇博客是实际工作中在游戏客户的研发团队沟通中遇到的一些问题,然后与Jerry深夜讨论,并由Jerry总结。知识点Sharing 一下。

问题描述

当请求里出现ws协议时,会出现http 504超时响应码。此问题只是经过SLB后会出现,直接请求后端Nginx服务没有此问题。

问题整体剖析

把server端nginx access log功能打开,对比从客户端直接访问和从SLB转发过来请求的access log,发现从客户端发起请求的是http1.1而从SLB转发过来的请求是http1.0,而出问题的都是http1.0,也就是说,请求经过SLB进入nginx时候协议降级到了1.0。
http1.0不支持keep-alive,如果使用http1.0发握手请求,服务端返回101以后就会直接结束这次HTTP会话了,所以出现了504超时

问题1:websocket和http两者关系是什么?

WebSocket是HTML5提出的协议,跟HTTP协议基本没有关系,下图可以说明他们之间的关系,为了兼容当前浏览器的握手规范,它是HTTP协议上的一种补充,有交集,但是并不是全部。~~
[AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第1张图片
下图可以说明他们的workload (再次感谢栗子哥截图)
[AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第2张图片

websocket 兼容性

WebSocket 跟其他 API 比较不一样的是,它不仅仅依赖于客户端的支持(例如浏览器),同时要求服务器和代理(ELB)支持。WebSocket 本质上跟 HTTP 完全不一样,只不过为了兼容性,WebSocket 的握手是以 HTTP 的形式发起的,如果服务器或者代理不支持 WebSocket,它们会把这当做一个不认识的 HTTP 请求从而优雅地拒绝掉。
websocket 如何利用HTTP进行握手(资料来源于WebSocket Wikipedia)
Client request
GET /chat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==Sec-WebSocket-Protocol: chat, superchatSec-WebSocket-Version: 13Origin: http://example.com
Server response
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: UpgradeSec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
这里重点说明当客户端发起HTTP请求时,header里出现Upgrade:websocket, 这就是websocket核心, 告诉server端,客户端发起的是websocket协议;服务端会返回一个状态码 101,告诉客户端我已经切换到websocket协议,至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行传输。

ws和wss的关系是什么?

Websocket使用 ws 或 wss 的统一资源标志符,类似于 HTTP 或 HTTPS,其中 wss 表示在 TLS 之上的 Websocket ,相当于 HTTPS 了。
默认情况下,Websocket 的 ws 协议使用 80 端口;运行在TLS之上时,wss 协议默认使用 443 端口。其实说白了,wss 就是 ws 基于 SSL 的安全传输,与 HTTPS 一样样的道理。
如果你的网站是 HTTPS 协议的,那你就不能使用 ws:// 了,浏览器会 block 掉连接,和 HTTPS 下不允许 HTTP 请求一样

问题2: HTTP, HTTP2.0, SPDY, HTTPS ?

HTTP
一般我们说到的HTTP,都是指的 1.0 和 1.1 两个版本。HTTP1.0最早在网页中使用是在1996年,HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议
HTTP特点(简单做概述)

  • HTTP的特点就是一个request一个response,如果在HTTP1.0 中,这次HTTP请求就结束了。
  • 如果是HTTP1.1,有了keep-alive的改进,也就是说在一次TCP连接里,可以发送多个request,接受多个response。
  • 但是请记住 request=response,在HTTP的世界里永远是这样的,一个request只能有一个response, 而且这个response是被动的,不能主动发起
  • HTTP1.0和HTTP1.1的一些区别
  • [AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第3张图片

  • HTTPS
    简单说为HTTP协议加一层SSL/TLS的外衣,实质的workload还是跑在HTTP协议上。因为HTTPS在发送HTTP请求前要建立SSL安全连接,所以连接方式和HTTP不太一样,所以走的是两个端口。
    HTTPS真的能降低用户对访问速度,并且增加服务器端CPU压力吗?
    这是肯定的,毕竟多了一层SSL握手;而且服务器端有大量的密钥计算,会消耗部分CPU资源
  • SPDY的诞生
    在讨论HTTP2.0之前,我们必须要知道 SPDY。2012年google提出了SPDY的方案,大家才开始从正面看待和解决老版本HTTP协议本身的问题,SPDY可以说是综合了HTTPS和HTTP两者有点于一体的传输协议,主要解决:
    1.降低延迟。SPDY采取了多路复用(multiplexing),多路复用通过多个请求stream共享一个tcp连接的方式
    2.请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。
    3.header压缩。前面提到HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大小和数量。
    4.基于HTTPS的加密协议传输,大大提高了传输数据的可靠性。
    5.服务端推送(server push)。采用了SPDY的网页,例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端6.再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。
    兼容性。SPDY位于HTTP之下,TCP和SSL之上,这样可以轻松兼容老版本的HTTP协议

  • HTTP2.0
    HTTP2.0在2015年被提出,可以说是SPDY的升级版(其实原本也是基于SPDY设计的),但是,HTTP2.0 跟 SPDY 仍有不同的地方,主要是以下两点:
  • HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS。但经过测试HTTP2.0也主要用于HTTPS,如果使用明文传输,服务器端自动降为HTTP1.1
  • HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE

    引发的问题3: 我如何确认我访问的站点是HTTP2.0或者SPDY?

    1. 安装浏览器插件
      HTTP/2 and SPDY indicator 是可以兼容chrome和firefox两个浏览器的插件,如果访问的站点是HTTP2.0或是SPDY,这个插件的闪电标会亮起,否则是灰色的
      原理剖析
      接下来大家还是要了解几个概念
  • *NPN
  • 谷歌开发了一个名为 下一代协议协商(Next Protocol Negotiation)的 SSL/TLS 扩展,SPDY 协议是基于 SSL/TLS 的,用于在客户端连接服务器时协商是否采用 HTTP/2 协议。SPDY 协议是由 Web 服务器所实现支持的,而 NPN 则是由 OpenSSL 等 SSL 实现支持的。*
  • ALPN
    SPDY 被提交到 IETF,然后变成了 HTTP/2 协议,谷歌也放弃了 SPDY 的开发,全力投入到了 HTTP/2 的开发中,之前所采用 NPN 也被一种新的协商协议 ALPN ——应用层协议协商(Application-Layer Protocol Negotiation)所替代。NPN 和 ALPN 是不兼容的。
  • NPN和ALPN对比
    NPN 是服务器发送所支持的协议列表,由客户端进行选择。
    ALPN 是客户端发送该列表,由服务端选择。
  • 兼容性
    NPN 已经广泛地被 OpenSSL 支持
    ALPN 则目前只有最新的 openssl-1.0.2 才支持
    主流操作系统openssl版本
    [AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第4张图片

NPN 和 ALPN 可以并存,但是会客户端会优先选择 ALPN。

  • SPDY与HTTP2.0 服务器端的支持情况,当前举例nginx
    在nginx官方网站的更新log里提到,nginx 1.9.5(2015-09-22),spdy模块(ngx_http_spdy_module)被替换为 httpv2模块(ngx_http_v2_module),如下图所示

浏览器的支持
Chrome 浏览器在2016年初取消对SPDY和NPN的支持,而专注于支持HTTP2.0和ALPN。引自(https://blog.chromium.org/2015/02/hello-http2-goodbye-spdy.html)
总结
如果使用2016年以后的浏览器,通过https方式访问服务端,并且想使用HTTP2.0协议,服务器端必须要使用 openssl-1.0.2 版本
如果以上条件都符合,但是访问方式为http明文方式,服务端会把协议版本降到1.1。
从侧面看出,当前HTTP2.0只能用于HTTPS的方式进行访问。

附录1:测试ALB是否支持HTTP2.0

如果使用https的方式访问ALB,结论如下图:
[AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第5张图片
Protocol : h2 的意思就是 HTTP2.0
如果使用http的方式访问ALB,会自动降到http1.1,结论如下图:

[AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第6张图片

附录2:如何开启chrome 显示元素里的Protocol标签

在标签栏右键,选中Protocol即可
[AWS][Ali云][SLB][ELB]由阿里SLB引发的Websocket问题系列总结_第7张图片

附录3:AWS ELB 支持的协议

CLB(Classic Load Balancer)
对于客户端连接至Classic Load Balancer,支持以下协议:HTTP/0.9、HTTP/1.0 和 HTTP/1.1。
ALB(Application Load Balancer)
对于客户端连接至Application Load Balancer, 支持以下协议:HTTP/0.9、HTTP/1.0、HTTP/1.1 和 HTTP/2。HTTP/2 仅适用于 HTTPS 侦听器,使用一个 HTTP/2 连接最多只能并行发送 128 个请求。Application Load Balancer 还支持将连接从 HTTP 升级到 Websockets。
对于已注册到负载均衡器的服务器
Application Load Balancer 和 Classic Load Balancer 均使用 HTTP/1.1。对于后端连接,默认支持保持连接。如果 HTTP/1.0 请求来自没有主机标头的客户端,负载均衡器会对后端连接发送的 HTTP/1.1 请求生成一个主机标头。
ALB对Websocket协议到支持
ALB支持原生的websocket,通过连接头为ws://和wss://的协议
以上AWS ELB相关资料来源
https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html
https://aws.amazon.com/blogs/aws/new-aws-application-load-balancer/

附录4:阿里SLB上使用Websocket注意事项:

如何在阿里云负载均衡上启用WS/WSS支持
无需配置,当选用HTTP监听时,默认支持无加密版本WebSocket协议(WS协议);当选择HTTPS监听时,默认支持加密版本的WebSocket协议(WSS协议)。
阿里SLB的实例类型:性能共享型和性能保障型
问题描述
当请求里出现ws协议时,会出现http 504超时响应码。此问题只是经过SLB后会出现,直接请求后端Nginx服务没有此问题。
问题剖析
把server端nginx access log功能打开,对比从客户端直接访问和从SLB转发过来请求的access log,发现从客户端发起请求的是http1.1而从SLB转发过来的请求是http1.0,而出问题的都是http1.0,也就是说,请求经过SLB进入nginx时候协议降级到了1.0。
http1.0不支持keep-alive,如果使用http1.0发握手请求,服务端返回101以后就会直接结束这次HTTP会话了,所以出现了504超时
结论
当使用ws时,需要将SLB实例升级为性能保障型实例。如果客户使用SLB性能共享型实例,很有可能会降低http版本.