HTTP

HTTP处理粘包

1、HTTP 协议通过设置回车符和换行符作为 HTTP header 的边界
2、通过 Content-Length 字段作为 HTTP body 的边界

HTTP缓存技术

对于一些重复HTTP请求,为了防止发送同样的HTTP请求,引入了缓存

强制缓存:只要浏览器判断缓存没过期,则直接用浏览器的本地缓存,浏览器决定是否使用缓存。通过请求资源的时间与 Cache-Control 中的过期时间大小,来计算出该资源是否过期

协商缓存:服务端告知客户端是否可以使用缓存,响应码是 304

与强制缓存的区别在于, 当缓存失效时客户端再次发送请求给服务器, 服务器只需要判断Etag资源是否被修改, 如果修改就返回新的资源并缓存, 否则只需要返回304状态码告诉客户端缓存依旧有效

( 只有在未能命中强制缓存时,才能发起带有协商缓存字段的请求 )

HTTP1.1的优点

1、简单:header + body ,头部信息也是 key-value 简单文本的形式

2、灵活和易于扩展:各类请求方法、URI/URL、状态码、头字段等都没被固定死,都允许开发人员自定义和扩充。

同时 HTTP 由于是工作在应用层( OSI 第七层),则它下层可以随意变化,比如:HTTPS 就是在 HTTP 与 TCP 层之间增加了 SSL/TLS 安全传输层;HTTP/1.1 和 HTTP/2.0 传输协议使用的是 TCP 协议,而到了 HTTP/3.0 传输协议改用了 UDP 协议

3、应用广泛和跨平台

HTTP1.1的缺点

1、无状态(好:减轻服务器负担,留出CPU和内存;坏:完成有关联性的操作时会麻烦<可用cookie>)

2、明文传输(好:方便抓包阅读;坏处:信息裸奔)

3、不安全:通信使用明文、不验证通信方的身份可能遭遇伪装、无法证明报文的完整性可能遭篡改

HTTP1.1的性能

1、长连接(只要一端没提出断开连接则保持 TCP 连接状态):1.0每发起请求就要新建一次TCP连接,且是串行请求,通信开销大。1.1使用长连接减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻服务器负载

2、管道pipeline网络传输:在同个TCP连接里客户端可发起多个请求,第一个请求发出去不必等其回来就可发第二个请求,减少整体响应时间,不过服务器必须按接收请求顺序发送对这些请求的响应。但是HTTP/1.1 管道解决了请求队头阻塞,没解决响应队头阻塞

HTTP1.1优化

1、避免发送HTTP请求:缓存。客户端把第一次请求以及响应数据保存在本地磁盘( key:请求 URL,value:响应)后续发起相同请求时就先在本地磁盘上通过 key 查到对应value,如果找到了就直接从本地读取响应。且会估算一个过期时间放在响应头

2、减少HTTP请求次数:减少重定向请求次数(重定向交由代理服务器完成,减少 HTTP 请求次数),合并请求(将多个小图合成一个大图来减少HTTP请求次数),延迟发送请求(按需获取)

3、减少HTTP响应的数据大小:有损压缩和无损压缩

HTTP2优化

1、头部压缩:同时发出多个请求头一样或相似,会消除重复部分。HPACK 算法:在客户端和服务器同时维护一张头信息表(字典),所有字段都存入这个表生成一个索引号,不发送同样字段只发索引号,再用 Huffman 编码压缩数据,提高速度

2、二进制格式:HTTP1.1纯文本形式报文,HTTP2 二进制格式,头和数据体都是二进制且统称为(头信息帧和数据帧),增加了数据传输效率

3、并发传输:解决HTTP1.1队头阻塞,引入Stream概念,1 个 TCP 连接含多个 Stream,Stream 里可含1个或多个Message(请求/响应)。Message 里包含一条或者多个帧Frame(Frame 是 HTTP/2 最小单位,以二进制压缩格式存放 HTTP/1 中的内容:头部和包体)

不同 HTTP 请求用独一无二的 Stream ID 来区分,接收端可通过 Stream ID 有序组装成 HTTP 消息,不同 Stream 的帧可乱序发送的(可并发不同的 Stream )

4、服务器主动推送资源:客户端和服务器双方都可建立Stream, Stream ID 区别:客户端Stream必须是奇数号,服务器Stream 必须是偶数号。

HTTP2缺陷

还存在队头阻塞问题,不过问题在 TCP 层。HTTP/2 基于 TCP 协议来传输数据,TCP 是字节流协议,必须保证收到字节数据完整且连续,这样内核才会将缓冲区里的数据返回给 HTTP 应用。

所以当「前 1 个字节数据」没到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据。

一旦发生丢包现象就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来

基于UDP协议实现可靠传输(HTTP3优化)

基于 UDP 协议在「应用层」实现了 QUIC 协议

HTTP/3 把 HTTP 下层TCP协议改成了UDP。UDP发送不管顺序,也不管丢包,所以不会出现像 HTTP/2 队头阻塞的问题。UDP不可靠传输的,但基于UDP的 QUIC 协议 可实现类似 TCP 的可靠性传输。

1、QUIC实现可靠传输:QUIC 通过单向递增的 Packet Number,配合 Stream ID 与 Offset 字段信息,可支持乱序确认而不影响数据包正确组装,摆脱了TCP必须按顺序确认应答 ACK 的限制,解决了 TCP 因某个数据包重传而阻塞后续所有待发送数据包的问题。

1、无队头阻塞:给每一 Stream 都分配一个独立的滑动窗口,当某个stream发生丢包时,只会阻塞这个stream,其他stream不会受影响,因此不存在队头阻塞问题

2、更快连接建立:对于 HTTP1 和 HTTP2 ,TCP和TLS分层。但 HTTP3的QUIC内包含了TLS,它在自己的帧会携带 TLS 里的记录,再加上 QUIC 使用的是 TLS1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,甚至在第二次连接时应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送达到 0-RTT 效果

3、连接迁移:基于 TCP 传输由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条连接。当移动设备网络从 4G 切换到 WIFI 时IP变了,就要断开连接然后重新建立连接。而建立连接包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡了一下,因此连接的迁移成本是很高的。

QUIC 没用四元组来绑定连接,而是通过连接 ID来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化导致IP变了,只要保有上下文信息(连接 ID、TLS 密钥等),就可以“无缝”复用原连接,消除重连成本,没有卡顿感。

有了HTTP为什么还要有RPC?

TCP面向连接、可靠且基于字节流,就最后一点容易产生粘包问题,从此基于TCP衍生出了很多协议比如HTTP(B/S)、RPC(C/S)

HTTP和RPC区别

1、服务发现:HTTP知道服务域名后可通过DNS服务器解析得到IP;RPC存一般有专门的中间服务去保存服务名和IP信息(Consul、Etcd,甚至Redis)想要访问某个服务,就去这些中间服务去获得 IP 和端口信息

2、底层连接形式:HTTP在建立底层 TCP 连接后会 keep alive复用连接;RPC也差不多,长链接,但一般还会再建个连接池。连接池利于提升网络请求性能,所以不少编程语言的网络库里都会给 HTTP 加个连接池,比如 Go 就是这么干的

3、传输内容:HTTP用json进行序列化(冗余啰嗦),RPC用 Protobuf(体积更小),同时也不需要像 HTTP 那样考虑各种浏览器行为,比如 302 重定向跳转啥的,性能更好

有了HTTP为什么还要有WebSocket?

WebSocket完美继承TCP全双工能力,且提供解决粘包方案。HTTP为半双工,对于大部分需要服务器主动推送数据到客户端的场景都不太友好,因此需要用支持全双工的 WebSocket 协议。