HTTP2和WebSocket

HTTP

http是目前应用最广泛的应用层协议,截止到目前为止已经发布了多个版本,最常用的是http1.1和http2。

http0.9是最早的版本,功能很简单,没有header,只支持GET。

http1.0

  • 只支持短连接,即每次请求一个资源就会新建一次tcp连接,服务器写完响应行后立刻将TCP连接关闭

    由于tcp连接的建立和关闭需要经历三次握手和四次握手,再加上tcp慢启动特性,报文一开始不会满负荷传输,所以不仅开销很大,而且效率很低。

  • 相比于上一版本,增加了http header,和status code用于声明请求的结果,content-type字段也可以用来声明传输其他文件,头部还增加了协议的版本号。

http1.1

http1.1是1997年发布的,是现在使用最广泛的版本,这个版本引入了长连接,减少了tcp连接建立和关闭的消耗。数据传输完成了保持TCP连接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据。

连接的本质是双方各自初始化了一个socket的标识符,可以当成一个文件,以后这个连接发送过来的东西都存到这个里面,所以说连接只是让双方确认一下,认识到以后有数据进来可以识别出来。发送请求其实就是先把要发送的数据存到一个缓冲区,然后等待操作系统帮你一层层打包发送到网卡然后走网线出去而已,接受就是一个相反的过程本质是一样的。

长连接模式下,连接关闭的主动权在客户端这里,当客户端收到服务器传来的响应时,如果不需要再次发送请求,那么它会发送一个connection:close的数据包来表示关闭连接。而为了避免TCP连接一直占用着浪费资源,服务器端也维护着一个keepalive_timeout参数,如果长时间没收到来自客户端的请求,服务器端也会主动关闭释放连接。

  • 新加了connection请求头字段,false表示短连接,keep-alive表示长连接(默认)

    在持久连接模式中,由于服务器不会立刻关闭TCP连接,所以需要在响应中加上一个Content-Length的响应头来表示响应体的长度,让浏览器判断HTTP响应是否结束。如果没有这个响应头的话,浏览器会处于pending的状态,因为在它看来还有数据要接收。

    所以长连接无非就是通过Content-Length字段把连接关闭的主动权交给了客户端。

  • 支持分块传输编码,可以发送动态数据,只在1.1版本中可以使用

    使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小,使得服务器可以发送动态生成的数据。

    设置相应头Transfer-Encoding:chunked,此时消息体便由数量未定的块组成,并以最后一个大小为0的块结束。

    HTTP/1.1 200 OK\r\n
    \r\n
    Transfer-Encoding: chunked\r\n
    ...\r\n
    \r\n
    <chunked 1 length>\r\n
    <chunked 1 content>\r\n
    25\r\n
    This is the data in the first chunk\r\n
    0\r\n
    \r\n
    

其他的改变还有增加了Host头,让服务端知道用户请求的是哪个域名等。

2014年,更新了内容,增加了TLS支持,即https传输,除了短连接和长连接模型,还支持服务端push模型,websocket模型。

补充问题1:k8s中长连接watch机制如何实现的?

http最大的特点就是它是一个拉协议,http无法做到服务端向客户端主动推送数据。但是由于http协议应用广泛,很多时候又想使用http协议去实现实时数据的获取,怎么办呢?

  • 轮询

    轮询是最普遍的基于HTTP协议采用拉的方式获取实时数据的,轮询又分为短轮询和长轮询。

    长轮询是服务器收到请求后,如果有数据会立刻响应请求。如果没有数据,就会把请求挂起一段时间,等待有没有数据产生,如果时间到了还没有数据,再响应http请求。长轮询的特点是实时性高,当然缺点就是会造成资源浪费。

    短轮询就是客户端按照一定时间间隔去请求http服务器,服务器会立即做出响应,不管有没有可用的数据。优点就是短连接,服务器处理方便。缺点是实时性低,很多无效请求,性能开销大。

  • http chunked

    前面已经介绍过分块传输编码了,使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。只要浏览器没有遇到结束标识,就会边解析边执行对应的响应内容。k8s的watch机制就是基于分块传输编码,当有新的数据产生时,发送一个数据块,而客户端在没遇到大小为0的数据块之前,会一直等待下一个数据块的到来。

补充问题2:什么情况下,客户端发送的http请求会复用同一个TCP连接?

  • 请求的是不同域名,肯定不复用

  • 请求的是相同域名,如果多个请求是并行的,就不复用。如果请求是一个一个完成的,那么就会复用。当然前提是http1.1 keep-alive情况。

    HTTP/1.1 存在一个问题,单个 TCP 连接在同一时刻只能处理一个请求,意思是说:两个请求的生命周期不能重叠,任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠。在 HTTP/1.1 存在 Pipelining 技术可以完成这个多个请求同时发送,但是由于浏览器默认关闭,所以可以认为这是不可行的。在 HTTP2 中由于 Multiplexing 特点的存在,多个 HTTP 请求可以在同一个 TCP 连接中并行进行。

http2

http2是2015年发布的,主要就是提升安全性与性能,gRPC底层就是使用的http2协议。

  • 多路复用(Multiplexing):就是说 HTTP/2 可以重复使用同一个 TCP 连接,并且连接是多路的,多个请求或响应可以同时传输,这也是gRPC支持客户端流和服务器端流的原因

    img

    • 对比之下,HTTP/1.1 的长连接也能复用 TCP 连接,但是只能串行,不能“多路”。

    • Rest API通常构建在http1.1上,是请求响应模式。如果一个微服务收到多个客户端请求,每次只能处理一个,即使构建在http2上,依旧是请求响应模式,无法发挥http2的优势:多路复用。

      gRPC构建在http2上,支持流式通信和双向通信,通过不断流式传输信息同时处理这些请求。但双向流模式依旧需要客户端发起调用。这里的流式通信,不是全双工的,只是将原本的请求响应风格,变为了同时可以发送多个请求,服务器也能发送多个响应。

  • 二进制协议:HTTP/2 的消息头使用二进制格式,而非文本格式。并且使用专门设计的 HPack 算法压缩。

  • 服务器推送:服务端能够直接把资源推送给客户端,当客户端需要这些文件的时候,它已经在客户端了。(HTTP/2推送服务器只能被浏览器来处理,而不是应用)

  • 通讯双方cache一份header filed表,用来差量更新头部字段,避免重复header的传输

HTTP3

2018年发布,基于谷歌的QUIC,底层使用udp代码tcp协议,

这样解决了队头阻塞问题,同样无需握手,性能大大地提升,默认使用tls加密。

WebSocket

websocket是一个双向通信协议,它在握手阶段采用http1.1

握手过程

  1. 发起握手请求

    HTTP/1.1 101 Switching Protocols  // 状态行
    Upgrade: websocket   // required
    Connection: Upgrade  // required
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // required,加密后的 Sec-WebSocket-Key
    Sec-WebSocket-Protocol: chat // 表明选择的子协议
    
  2. 服务器如果支持websocket,返回101响应

    HTTP/1.1 101 Switching Protocols  // 状态行
    Upgrade: websocket   // required
    Connection: Upgrade  // required
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // required,加密后的 Sec-WebSocket-Key
    Sec-WebSocket-Protocol: chat // 表明选择的子协议
    

WebSocket 提供两种协议:不加密的 ws:// 和 加密的 wss://. 因为是用 HTTP 握手,它和 HTTP 使用同样的端口:ws 是 80(HTTP),wss 是 443(HTTPS)

HTTP2 vs WebSocket

WebSocket是全双工的,可以双向通信,主要应用在实时通信的场景中,服务器可以实时推送数据给客户端。

HTTP/2 虽然也支持 Server Push,但是服务器只能主动将资源推送到客户端缓存!那不是应用程序可以感知的,主要是让浏览器(用户代理)提前缓存静态资源。

在典型的HTTP 1.x工作流中,浏览器请求一个页面,服务器在响应中返回一个HTML,然后就时等待浏览器解析响应并发送额外请求来获取额外的内嵌资源(JavaScript、CSS等)。服务器推送使服务器能够试探性地向客户端发送资源。此时,浏览器不必解析HTML页面并找到需要加载的其它资源;而是服务器能够立即开始发送它们。

但是人类的想象力是非常丰富的。比如为了让服务器可以推送消息给客户端,使用了一个“偏方”。它就是SSE(Server Sent Event)。SSE是一种让服务器能够在客户端服务器建立连接之后异步推送数据给客户端的机制。由于SSE是基于HTTP的,其天然适配于HTTP/2,这样SSE就可以集两者之长:HTTP/2可以基于多路复用流形成一个高效传输层,同时SSE给应用提供了API使之能够进行推送。

Websocket技术可能会继续使用,但是SSE和其EventSource API同HTTP/2的能力相结合可以在多数场景下达到同样的效果,但是会更简单。

posted @ 2022-01-13 21:02  尹瑞星  阅读(3330)  评论(0编辑  收藏  举报