HTTP 只解决了「取一个资源」,别的需求它做不了:要实时、要并行、要改服务器上的文件, 它都没有对应的表达方式。所以人们在它周围打补丁——有的换协议,有的换传输,有的只加方法。
前八章全在 HTTP 内部拆零件。 这一章抬头往外看:HTTP 之外长了哪些东西,它们各自补的是哪一块短板。
一、HTTP 的目标其实很窄
从第一章到第八章,HTTP 一直在做同一件事:客户端说「我要哪个资源」,服务端把它送回来。 方法就那么几个(2.5),全是围绕「操作资源」设计的。 这套设计活了二十多年,因为当年的页面就是文档。 可页面变成应用之后,需求换了三样:要实时(聊天、行情、协同编辑), 要快(一个页面几十上百个资源),要能写(在线改服务器上的文件)。 这三样 HTTP 一样都答不好。不是实现得糙,是协议里根本没有这个表达方式——就像用信封送不了直播。
二、补丁打在三个不同的地方
第一类:应用层换一个协议,但借 HTTP 的门进来。 代表是 WebSocket,握手那一下还是 HTTP 请求,谈成之后这条连接就不再跑 HTTP 了(9.3)。 第二类:语义不动,改传输方式。 代表是 SPDY,它把请求切成帧、在一条连接上交错发送、压首部、还能主动推送; 这套东西后来变成了 HTTP/2.0(9.2、9.4)。 第三类:协议主体不动,只加方法和状态码。 代表是 WebDAV,专门管「服务器上的文件」,加了 MKCOL、COPY、MOVE、LOCK 这些(9.5)。 三类的野心依次递减:换协议最彻底,也最难推;加几个方法最轻,但只能解决一个垂直场景。
三、为什么都要借 HTTP 的门
一个新协议最大的成本不是写代码,是让它在真实网络里跑得通。
80 和 443 这两个端口几乎所有防火墙都放行,别的端口经常被拦。
还有一整套现成的东西:代理认得 HTTP(5.2.1),缓存认得,认证认得,浏览器更是只肯为一个协议做特例。
绕开它,等于把这些全部重写一遍。
所以新协议几乎都以一个 HTTP 请求开局,谈成了再换——这叫协议升级,
Upgrade 首部就是干这个的(6.3.7)。WebSocket 和 HTTP/2 的 h2c 都走这条路。
四、动手 Demo:三个数字,看出 HTTP 的三处短板
一个只会「问一句答一句」的本机服务端,每次处理故意花 20 毫秒。 先用一条连接串行发 8 个请求,再用 8 条连接并行发同样的 8 个,对比总耗时。 然后连上去一句话不说,看服务端会不会主动发点什么。最后量一下一条真实浏览器请求的首部有多少字节。
# 串行:一条连接,一个一个来
s = socket.create_connection((u'127.0.0.1', port))
for i in range(8):
s.sendall(req(u'/item%d' % i))
recv_response(s)
# 并行:八条连接,各发各的
for i in range(8):
Thread(target=one, args=(i,)).start()
实跑输出:
--------------------------------------------------------------------
一条连接串行,还是八条连接并行
--------------------------------------------------------------------
服务端每次处理耗时 20 毫秒(Demo 里故意加的)
串行 8 个请求 193.0 毫秒
并行 8 个请求 42.3 毫秒
串行/并行 4.6 倍
沉默 350 毫秒收到 0 字节
沉默之后发一个请求 HTTP/1.1 200 OK
最后一行是本节的重点:连着但不说话,服务端一个字都不会主动发过来。
HTTP 是「问一句答一句」,所以服务器有新数据时只能等下次被问。
一条浏览器式请求首部 232 字节
正文(一个「ok」) 2 字节
首部是正文的 116 倍
串行 193.0 毫秒,并行 42.3 毫秒,差 4.6 倍。
差别不在带宽,在「等」——串行时八次等待叠在一起,并行时它们重叠了。
沉默 350 毫秒,收到 0 字节。服务端明明连着,就是不发。
这是 HTTP 的根本约束:它没有「我给你推一条」这种语法。
沉默之后发一个请求,立刻回 HTTP/1.1 200 OK。所以它不是不会说话,是只肯被问。
这一条决定了后面所有实时方案的样子。
一条浏览器式请求的首部 232 字节,正文只有 2 字节,首部是正文的 116 倍。
一个页面几十个资源,这份路费要重复付几十遍。
30 秒实验
- 把服务端那 20 毫秒改成 200 毫秒,看串行与并行的倍数会不会变。
- 把并行那组的线程数改成 2,看总耗时怎么变——为什么不是简单的四分之一?
- 数一数你自己打开一个常用网站时,浏览器发了多少个请求。
常见误区
- 「HTTP 慢是因为带宽不够」——多数时候是等,不是挤。
- 「服务端可以随时推数据给客户端」——不能,HTTP 只有应答,没有主动发送。
- 「新协议都是推倒重来」——绝大多数都借 HTTP 的门进来,再谈升级。
自测三问(点开对答案)
1. HTTP 最根本的交互方式是什么? —— 客户端问、服务端答。没有「服务端主动发」这种语法。 2. 这一章讲的三个方向分别改的是什么? —— WebSocket 换应用层协议,SPDY/HTTP/2 改传输方式,WebDAV 只加方法和状态码。 3. 为什么新协议常常要先发一个 HTTP 请求? —— 为了借用端口、代理、缓存、认证这些现成的基础设施,谈成再升级。先别急着看新协议。得先把「HTTP 到底卡在哪」量清楚,否则不知道补丁要打在哪儿。
- ☐ (本节完,下一节 9.2.1 HTTP的瓶颈)