长连接出现的前夜
在了解长连接之前,我们先看看与之对应的短连接是什么?在HTTP协议的初始版本中,每进行一次HTTP通信就要断开一次TCP连接。早前的通信情况来看,因为都是些容量很小的文本传输,所以没有太大的问题,但是随着HTTP传输文档中包含大量的富文本,比如使用浏览器浏览一个包含多张图片的HTML页面的时候,在发送请求访问HTML页面资源的同时,也会请求该HTML页面包含的其他资源,因此,每次的请求都会造成无谓的TCP连接的建立和断开,增大了开销。
keep-alive字段
为了解决上面的问题,有些浏览器在请求的时候,使用了一个非标准的Connection字段。
Connection: keep-alive
上面的字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。
Connection: keep-alive
这样一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接,但是这不是标准的字段,不同实现的行为可能不一致,因此不是根本的解决办法。
长连接出现了
1997年1月,HTTP/1.1版本发布了,它进一步完善了HTTP协议,直到现在还是最流行的版本。HTTP/1.1版的最大变化就是引入了持久连接(HTTP Persistent Connections),即TCP默认连接不关闭,可以被多个请求复用,不用声明keep-alive字段。
持久连接的好处
- 减少了TCP连接的重复建立和断开造成的额外开销,减轻了服务器端的负载。
- 使得HTTP请求和响应能够更早的结束,这样web页面的显示速度也就对应的提高了。
客户端和服务器端发现对方一段时间内没有活动,就可以主动关闭连接。不过规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。
Connection: close
目前,对同一个域名,大多数浏览器允许同时建立6个持久连接。
管道机制
注意:管道机制是基于持久连接的HTTP/1.1版还引入了管道机制,即在同一个TCP连接里面,客户端可以同时发送多个请求,这样就进一步改进了HTTP协议的效率,以前发送请求后需要等待并接收响应,才能发送下一个请求。管线化技术出现后,不用等待响应即可直接发送下一个请求,这样就能够做到同时并行发送多个请求,而不需要一个接一个的等待响应了,与挨个连接相比,用持久连接可以让请求更快结束。而管线化技术则比持久连接要快的多,请求数越多,时间差就越明显。
案例
假如一个客户端需要请求两个资源,以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出相应,收到后再发出B请求,管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求再回应B请求。
Content-Length字段
一个TCP连接可以传回多个响应,势必就要有一种机制,区分数据包是属于哪一个响应的,这就是Content-Length字段的作用,声明本次回应的数据长度。
Content-Length: 3495
上面的代码告诉浏览器,本次响应的长度是3495个字节,后面的字节就属于下一个回应了。在1.0版本中,Content-Length字段不是必须的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了。
分块传输编码
使用Content-Length字段的前提条件是,服务器发送响应之前,必须知道响应的数据长度,对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,这样的效率不行,更好的处理方法是,产生一块数据,就发送一块,采用流模式代替缓存模式。因此,1.1版本规定可以不使用Content-Length字段,而是使用分块传输编码,只要请求或响应的头信息中有Transfer-Encoding字段,就表明回应将由数量未定的数据块组成。
Transfer-Encoding: chunked
每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度,最后是一个大小为0的块,就表示本次回应的数据发送完了。
长连接带来的问题
虽然HTTP1.1版本允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按照次序进行的,所以服务器只有处理完一个响应,才会进行下一个响应,如果前面的响应特别慢,后面就会有许多请求排队等待着,这就称之为队头阻塞。
如何避免?
- 减少请求数。
- 同时多开持久连接。