本文主要是参考Google SPDY项目主页的一些文档总结而来,目的整体上介绍SPDY协议的定义为主。后续,我将写一系列的文章分析SPDY的一些项目(如:Nginx),SPDY的性能测试以及如何部署SPDY到实际生产应用。
一、HTTP协议存在的一些主要问题
1、一个连接一个请求。浏览器和web server之间都是以短连接方式交互,一个连接只服务一次请求,对于一个需要加载多个资源的页面来说,将会带来很高的延迟。
2、只能由客户端发起请求,服务器完成响应。服务器不能主动的将一些必须的资源推送给客户端。
3、HTTP协议只能对body进行压缩处理,不能压缩header。在一个cookie较多的站点,将对带宽造成严重的浪费。
http的问题在10年前的web中,是无关紧要的。然而今天的web已不再像10年的web那般简单了,同时用户对web应用的体验要求也将是越来越高,所以Google发起了“Let's make the web faster“项目,SPDY正是此项目的一部分。
二、SPDY的目标
起初,SPDY项目并不只是为了加速http,更多的是要致力于改进传输层和会话层协议。比如: Stream Control Transmission Protocol (SCTP), 一个用于替换TCP的传输层协议。除此之外还有一些其他方面的研究,可以在相应的white paper上找到。开发一个新的传输层协议也许很简单,但考虑到后期的部署、推广和兼容历史等因素,这估计是一个非常糟糕的决定,等待的结果必然是失败。因此,目前的SPDY主要还是集中应用层协议的改进,加速HTTP。
SPDY的主要目标:
1、单个tcp连接支持并发的Http请求。这主要是解决目前http协议的一个连接只能服务一次请求的问题。
2、压缩请求/响应头。
3、定义spdy为一个相比http容易实现的协议。
4、使用ssl加密连接,用户数据更加安全的同时也解决了对已有网络体系的兼容问题。其实,TLS的强制,更多的是解决历史兼容问题,而不是安全考虑,后面详细介绍。
5、服务器可以主动的向客户端发起连接,并推送数据。
三、SPDY协议栈
在http协议栈中引入spdy后,大概就是下图(来自Google官方)的样子:
关键就是在tcp连接上面加入了spdy session层,这一层根据spdy draft又可以称为framing层,主要提供stream机制和消息组帧的实现。下文介绍具体协议的时候,再谈。
四、TLS-NPN
TLS-NPN全称是Transport Layer Security - Next Protocol Negotiation,NPN是Google为了支持SPDY协议作为一个应用层协议使用,而为TLS定义的一个扩展。NPN扩展的出现仅仅是为了更好的部署和使用SPDY,任何时候它都不会是SPDY的一部分。关于NPN扩展的详细介绍请看这里:http://technotes.googlecode.com/git/nextprotoneg.html 。
既然TLS-NPN并不是SPDY协议的一部分,那为什么要强制TLS?为何不能像HTTP一样根据用户自由选择是否SSL呢?Google这样做,必然是有道理的:
1、目前整个互联网可以说只有80和443两个端口可用,如果SPDY跑在一个新的端口上提供web服务,就极有可能会被诸如防火墙等设施给阻止掉。要想推动整个网络系统引入一个新的等同于80和443的标准端口,我想这绝对不是一件容易的事情,因此聪明的Google必须考虑兼容现在的网络体系环境,而不是自作聪明的推动新的端口。
2、放弃使用新端口的方案,就只能在80和443上面选择了。然而,目前很多的web中间代理设施只认80端口跑http协议,一旦SPDY跑在80端口上,必然会被这些代理给阻止掉。80端口显然也不能使用。
3、没了80端口,就只能选择443了,443目前是https协议加密数据传输,理论上443端口上经过加密的套接字可以传输任何应用层协议数据。
因此,为了在SSL上能够协商使用SPDY协议,Google就定义了NPN扩展,此扩展目前已经在OpenSSL、NSS等实现了。
五、SPDY-draft2
Google已经放出的SPDY协议草案在此: http://www.chromium.org/spdy/spdy-protocol ,目前已经到draft4了,但本文参考的是draft2,因为目前Nginx的SPDY实现是基于draft2而来。
SPDY在逻辑上由部分构成,一是framing layer,其主要负责组装帧消息,为了request/response提供基础;二是HTTP layer,它定义了request/response的相关行为。
根据上面这个简单的模型,从总体上看一下SPDY的3个重要组成部分——TCP connection、framing layer、http layer。
上图描述的是client和server之间的一个TCP连接;这个TCP连接上面有两个stream,一个stream就负责了一次request/response;一个request/response由多个frame(一个小箭头就是一个frame)组成;frame又分为了control frame (红色箭头)和data frame (蓝色箭头)。framing layer主要是定义一个frame消息,HTTP layer主要是定义request/response。
一次request/response完成后,就释放此stream,但不会释放TCP connection。要发起新的request,首先得建立一个stream,由此可看出SPDY在TCP connection上加入了stream机制来模拟了http的短连接。stream其实只是TCP上面的一个逻辑层,它的建立和释放非常快,没有TCP的多次握手,这就解决了TCP的Slow Start问题。换个角度想想,如果TCP connection的建立和释放本来就无延迟,非常的快速,那么http就没有改进的必要了,我猜想这也是一开始Google想从传输协议TCP入手解决问题的考虑。
Framing
Framing分为control和data两种,下面详细介绍SPDY定义了哪些control frame和data frame,他们的作用是什么。
control frame结构
+----------------------------------+
|C| Version(15bits) | Type(16bits) |
+----------------------------------+
| Flags (8) | Length (24 bits) |
+----------------------------------+
| Data |
+----------------------------------+
上图可以看到一个control frame至少有8个字节的头部信息。
C: 占一个bit,用来识别此frame是control还是data之用。
Version: 占15个bit,是SPDY的版本号,目前是2。
Type: 占16个bit,control frame的类型,表示此control frame具体是干什么用,后面将介绍每种类型的control frame。
Flags: 占8个bit,一个附加的属性域。
Length: 占24个bit,其实是一个无符号的24位整数,表示Length域后面的数据长度。
data frame结构
+----------------------------------+
|C| Stream-ID (31bits) |
+----------------------------------+
| Flags (8) | Length (24 bits) |
+----------------------------------+
| Data |
+----------------------------------+
同样可以看出一个data frame至少也有8个字节的头部信息。
C: 同control frame。
Stream-ID: 占31bit,一个stream的标志id。
Flags和Length: 同control frame。
stream
SPDY的一个stream既可以被client创建,也可以被server创建,这就说明SPDY不但允许由client发起请求,也可以由server发起请求。如果你愿意,还可以创建一个单向stream,只需要发送特定的control frame就可以了,单向stream可以认为是只有请求,永远不会有响应。stream的创建、关闭等操作都是由control frame实现的。
control frames
SPDY定义了不少的control frames,这里简单的列举一下它们,不作详细的介绍,如果你在实现SPDY的时候,请求看具体draft。
SYN_STREAM control frame -
是发送者用来创建一个stream。
SYN_REPLY control frame - SYN_STREAM接受者用此control frame去开始响应对方。
RST_STREAM control frame - 可以用来中断一个stream。
SETTINGS control frame - 可以用来改变一个stream的状态,属性等。
NOOP control frame - 是一个无操作的control frame了,接受者直接忽略它。
PING control frame - 类似ping命令的功能,可以用来测量一个rtt时间。ping工具是工作在4层,此处的PING control frame却是工作在7层上。
GOAWAY control frame - 发送者用来告诉对端,不再接受新的stream了。可以理解为不久将要关键当前的TCP connection。
HEADERS control frame - 给stream添加一些附加的header。
到此,各种control frame都简单的介绍完了,每个frame的具体定义还得参考draft。至于datat frame,相比control frame就简单多了,它没有这么类型,它只做一件事情——就是在某个stream上传输数据。
HTTP layer
为了兼容目前的所有应用,尽量做到应用不做任何修改,或者尽可能少的修改。因此http layer定义了如何将http request/response跑在framing之上。
客户端通过SYN_STREAM control frame发起一个请求,请求相关的所有header放在SYN_STREAM frame中发送,如果请求有body将在data frame中发送,最后一个data frame必须设置FIN_FLAG标志请求结束。
服务器通过SYN_REPLY control frame响应客户端,响应的所有header放在SYN_REPLY frame中发送,响应数据通过data frame来发送,同样最后一个data frame需要设置FIN_FLAG标志响应结束。
SPDY还详细的定义http request/response headers的异同,具体看草案。
server push
有了push功能就可以针对一个客户端请求响应多个应答,这样可以避免针对每个资源的获取,客户端都要主动来请求一次;相反,服务器主动将这样一些明知客户端需要的资源推送给客户端。push功能的实现感觉还是挺复杂的,具体参考spdy-draft。
SPDY 部署
1、服务器通过Alternate-Protocol header来建议使用SPDY
服务器收到一个普通的http请求的时候,可以通过在响应header里添加Alternate-Protocl来告知客户端可以发送SPDY。客户端收到Alternate-Protocl header后,就尝试去通过SPDY发送请求给服务器,如果失败了,在当前域下,就再也不要去尝试做这件事情了。
2、服务器通过TLS-NPN扩展来建议使用SPDY
服务器在443端口收到一个携带有TLS-NPN扩展的连接,就告诉客户端自己所支持的spdy版本号,然后客户端就通过SPDY来和服务器通信。通过TLS-NPN扩展的方式应该是唯一的途径在整个互联网上使用。Alternate-Protocol的方式只有在确定客户端和服务器中间没有拒绝其他端口的防火墙等设施才ok。
SPDY的现状
目前Google,twitter,facebook等公司都已经开始着手部署spdy来加速自己的web应用了。在服务器方面,Apache、Nginx、Netty、Jetty、node.js等都已经开始初步的支持SPDY。浏览器方面,chrome已经支持SPDY,firefox貌似也开始支持了。其次,在github上搜索一下SPDY,可以看到有不少spdy的项目存在了。