本节书摘来华章计算机《计算机网络:自顶向下方法(原书第6版)》一书中的第2章 ,第2.2节,(美)james f.kurose keith w.ross 著 陈 鸣 译 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
20世纪90年代以前,因特网的主要使用者还是研究人员、学者和大学生,他们登录远程主机,在本地主机和远程主机之间传输文件,收发新闻,收发电子邮件。尽管这些应用非常有用(并且继续如此),但是因特网基本上不为学术界和研究界之外所知。到了20世纪90年代初期,一个主要的新型应用即万维网(world wide web)登上了舞台[berners-lee 1994]。web是一个引起公众注意的因特网应用,它极大地改变了人们与工作环境内外交流的方式。它将因特网从只是很多数据网之一的地位提升为仅有的一个数据网。
也许对大多数用户来说,最具有吸引力的就是web的按需操作。当用户需要时,就能得到所想要的内容。这不同于无线电广播和电视,迫使用户只能收听、收看内容提供者提供的节目。除了可以按需操作以外,web还有很多让人们喜欢和珍爱的特性。任何人使信息在web上可用都非常简单,即只需要极低的费用就能成为出版人。超链接和搜索引擎帮助我们在web站点的海洋里导航。图形化的界面刺激着我们的感官。表单、java小程序和很多其他的装置,使我们可以与web页面和站点进行交互。并且web为许多在2003年以后出现的招人喜爱的应用提供了平台,这些应用包括youtube、gmail和face book(脸谱)。
web的应用层协议是超文本传输协议(hypertext transfer protocol,http),它是web的核心,在[rfc 1945]和[rfc 2616]中进行了定义。http由两个程序实现:一个客户程序和一个服务器程序。客户程序和服务器程序运行在不同的端系统中,通过交换http报文进行会话。http定义了这些报文的结构以及客户和服务器进行报文交换的方式。在详细解释http之前,应当回顾某些web术语。

http定义了web客户向web服务器请求web页面的方式,以及服务器向客户传送web页面的方式。我们稍后详细讨论客户和服务器的交互过程,而其基本思想在图2-6中进行了图示。当用户请求一个web页面(如点击一个超链接)时,浏览器向服务器发出对该页面中所包含对象的http请求报文,服务器接收到请求并用包含这些对象的http响应报文进行响应。
http使用tcp作为它的支撑运输协议(而不是在udp上运行)。http客户首先发起一个与服务器的tcp连接。一旦连接建立,该浏览器和服务器进程就可以通过套接字接口访问tcp。如同在2.1节中描述的那样,客户端的套接字接口是客户进程与tcp连接之间的门,在服务器端的套接字接口则是服务器进程与tcp连接之间的门。客户向它的套接字接口发送http请求报文并从它的套接字接口接收http响应报文。类似地,服务器从它的套接字接口接收http请求报文和向它的套接字接口发送http响应报文。一旦客户向它的套接字接口发送了一个请求报文,该报文就脱离了客户控制并进入tcp的控制。2.1节讲过,tcp为http提供可靠数据传输服务。这意味着,一个客户进程发出的每个http请求报文最终能完整地到达服务器;类似地,服务器进程发出的每个http响应报文最终能完整地到达客户。这里我们看到了分层体系结构最大的优点,即http协议不用担心数据丢失,也不关注tcp从网络的数据丢失和乱序故障中恢复的细节。那是tcp以及协议栈较低层协议的工作。
注意到下列现象很重要:服务器向客户发送被请求的文件,而不存储任何关于该客户的状态信息。假如某个特定的客户在短短的几秒钟内两次请求同一个对象,服务器并不会因为刚刚为该客户提供了该对象就不再做出反应,而是重新发送该对象,就像服务器已经完全忘记不久之前所做过的事一样。因为http服务器并不保存关于客户的任何信息,所以我们说http是一个无状态协议(stateless protocol)。我们同时也注意到web使用了客户-服务器应用程序体系结构(如2.1节所述)。web服务器总是打开的,具有一个固定的ip地址,且它服务于可能来自数以百万计的不同浏览器的请求。
在许多因特网应用程序中,客户和服务器在一个相当长的时间范围内通信,其中客户发出一系列请求并且服务器对每个请求进行响应。依据应用程序以及该应用程序的使用方式,这一系列请求可以以规则的间隔周期性地或者间断性地一个接一个发出。当这种客户-服务器的交互是经tcp进行的,应用程序的研制者就需要做一个重要决定,即每个请求/响应对是经一个单独的tcp连接发送,还是所有的请求及其响应经相同的tcp连接发送呢?采用前一种方法,该应用程序被称为使用非持续连接(non-persistent connection);采用后一种方法,该应用程序被称为使用持续连接(persistent connection)。为了深入地理解该设计问题,我们研究在特定的应用程序即http的情况下持续连接的优点和缺点,http既能够使用非持续连接,也能够使用持续连接。尽管http在其默认方式下使用持续连接,http客户和服务器也能配置成使用非持续连接。
1.采用非持续连接的http
我们看看发生了什么情况:
http客户进程在端口号80发起一个到服务器www.someschool.edu的tcp连接,该端口号是http的默认端口。在客户和服务器上分别有一个套接字与该连接相关联。
http客户经它的套接字向该服务器发送一个http请求报文。请求报文中包含了路径名/somedepartment/home.index(后面我们会详细讨论http报文)。
http服务器进程经它的套接字接收该请求报文,从其存储器(ram或磁盘)中检索出对象www.someschool.edu/somedepartment/home.index,在一个http响应报文中封装对象,并通过其套接字向客户发送响应报文。
http服务器进程通知tcp断开该tcp连接。(但是直到tcp确认客户已经完整地收到响应报文为止,它才会实际中断连接。)
http客户接收响应报文,tcp连接关闭。该报文指出封装的对象是一个html文件,客户从响应报文中提取出该文件,检查该html文件,得到对10个jpeg图形的引用。
对每个引用的jpeg图形对象重复前4个步骤。
当浏览器收到web页面后,显示给用户。两个不同的浏览器也许会以不同的方式解释(即向用户显示)该页面。http与客户如何解释一个web页面毫无关系。http规范([rfc 1945]和[rfc 2616])仅定义了在http客户程序与http服务器程序之间的通信协议。
上面的步骤举例说明了非持续连接的使用,其中每个tcp连接在服务器发送一个对象后关闭,即该连接并不为其他的对象而持续下来。值得注意的是每个tcp连接只传输一个请求报文和一个响应报文。因此在本例中,当用户请求该web页面时,要产生11个tcp连接。
在上面描述的步骤中,我们有意没有明确客户获得这10个jpeg图形对象是使用10个串行的tcp连接,还是某些jpeg对象使用了一些并行的tcp连接。事实上,用户能够配置现代浏览器以控制并行度。在默认方式下,大部分浏览器打开5~10个并行的tcp连接,而每条连接处理一个请求响应事务。如果用户愿意,最大并行连接数可以设置为1,这样10条连接就会串行建立。我们在下一章会看到,使用并行连接可以缩短响应时间。
在继续讨论之前,我们来简单估算一下从客户请求html基本文件起到该客户收到整个文件止所花费的时间。为此,我们给出往返时间(round-trip time,rtt)的定义,该时间是指一个短分组从客户到服务器然后再返回客户所花费的时间。rtt包括分组传播时延、分组在中间路由器和交换机上的排队时延以及分组处理时延(这些在1.4节已经讨论过)。现在考虑当用户点击超链接时会发生什么现象。如图2-7所示,这引起浏览器在它和web服务器之间发起一个tcp连接;这涉及一次“三次握手”过程,即客户向服务器发送一个小tcp报文段,服务器用一个小tcp报文段做出确认和响应,最后,客户向服务器返回确认。三次握手中前两个部分所耗费的时间占用了一个rtt。完成了三次握手的前两个部分后,客户结合三次握手的第三部分(确认)向该tcp连接发送一个http请求报文。一旦该请求报文到达服务器,服务器就在该tcp连接上发送html文件。该http请求/响应用去了另一个rtt。因此,粗略地讲,总的响应时间就是两个rtt加上服务器传输html文件的时间。
2.采用持续连接的http
非持续连接有一些缺点。首先,必须为每一个请求的对象建立和维护一个全新的连接。对于每个这样的连接,在客户和服务器中都要分配tcp的缓冲区和保持tcp变量,这给web服务器带来了严重的负担,因为一台web服务器可能同时服务于数以百计不同的客户的请求。第二,就像我们刚描述的那样,每一个对象经受两倍rtt的交付时延,即一个rtt用于创建tcp,另一个rtt用于请求和接收一个对象。
在采用持续连接的情况下,服务器在发送响应后保持该tcp连接打开。在相同的客户与服务器之间的后续请求和响应报文能够通过相同的连接进行传送。特别是,一个完整的web页面(上例中的html基本文件加上10个图形)可以用单个持续tcp连接进行传送。更有甚者,位于同一台服务器的多个web页面在从该服务器发送给同一个客户时,可以在单个持续tcp连接上进行。可以一个接一个地发出对对象的这些请求,而不必等待对未决请求(流水线)的回答。一般来说,如果一条连接经过一定时间间隔(一个可配置的超时间隔)仍未被使用,http服务器就关闭该连接。http的默认模式是使用带流水线的持续连接。我们把量化比较持续连接和非持续连接性能的任务留作第2、3章的课后习题。鼓励读者阅读文献[heidemann 1997;nielsen 1997]。
http规范[rfc 1945;rfc 2616]包含了对http报文格式的定义。http报文有两种:请求报文和响应报文。下面讨论这两种报文。
1.http请求报文
下面提供了一个典型的http请求报文:
通过仔细观察这个简单的请求报文,我们就能知道很多东西。首先,我们看到该报文是用普通的ascii文本书写的,这样有一定计算机知识的人都能够阅读它。其次,我们看到该报文由5行组成,每行由一个回车和换行符结束。最后一行后再附加一个回车换行符。虽然这个特定的报文仅有5行,但一个请求报文能够具有更多的行或者至少为一行。http请求报文的第一行叫做请求行(request line),其后继的行叫做首部行(header line)。请求行有3个字段:方法字段、url字段和http版本字段。方法字段可以取几种不同的值,包括get、post、head、put和delete。绝大部分的http请求报文使用get方法。当浏览器请求一个对象时,使用get方法,在url字段带有请求对象的标识。在本例中,该浏览器正在请求对象/somedir/page.html。其版本字段是自解释的;在本例中,浏览器实现的是http/1.1版本。
现在我们看看本例的首部行。首部行host:www.someschool.edu指明了对象所在的主机。你也许认为该首部行是不必要的,因为在该主机中已经有一条tcp连接存在了。但是,如我们将在2.2.5节中所见,该首部行提供的信息是web代理高速缓存所要求的。通过包含connection:close首部行,该浏览器告诉服务器不希望麻烦地使用持续连接,它要求服务器在发送完被请求的对象后就关闭这条连接。user-agent:首部行用来指明用户代理,即向服务器发送请求的浏览器的类型。这里浏览器类型是mozilla/5.0,即firefox浏览器。这个首部行是有用的,因为服务器可以有效地为不同类型的用户代理实际发送相同对象的不同版本。(每个版本都由相同的url寻址。)最后,accept-language:首部行表示用户想得到该对象的法语版本(如果服务器中有这样的对象的话);否则,服务器应当发送它的默认版本。accept-language:首部行仅是http中可用的众多内容协商首部之一。
看过一个例子之后,我们再来看看如图2-8所示的一个请求报文的通用格式。我们看到该通用格式与我们前面的例子密切对应。然而,你可能已经注意到了在首部行(和附加的回车和换行)后有一个“实体体”(entity body)。使用get方法时实体体为空,而使用post方法时才使用该实体体。当用户提交表单时,http客户常常使用post方法,例如当用户向搜索引擎提供搜索关键词时。使用post报文时,用户仍可以向服务器请求一个web页面,但web页面的特定内容依赖于用户在表单字段中输入的内容。如果方法字段的值为post时,则实体体中包含的就是用户在表单字段中的输入值。
当然,如果不提“用表单生成的请求报文不是必须使用post方法”这一点,那将是失职。html表单经常使用get方法,并在(表单字段中)所请求的url中包括输入的数据。例如,一个表单使用get方法,它有两个字段,分别填写的是“monkeys”和“bananas”,这样,该url结构为www.somesite.com/animalsearch?monkeys&bananas。在日复一日的网上冲浪中,你也许已经留意到了这种扩展的url。
head方法类似于get方法。当服务器收到使用head方法的请求时,将会用一个http报文进行响应,但是并不返回请求对象。应用程序开发者常用head方法进行调试跟踪。put方法常与web发行工具联合使用,它允许用户上传对象到指定的web服务器上指定的路径(目录)。put方法也被那些需要向web服务器上传对象的应用程序使用。delete方法允许用户或者应用程序删除web服务器上的对象。
2.http响应报文
下面我们提供了一条典型的http响应报文。该响应报文可以是对刚刚讨论的例子中请求报文的响应。
我们仔细看一下这个响应报文。它有三个部分:一个初始状态行(status line),6个首部行(header line),然后是实体体(entity body)。实体体部分是报文的主要部分,即它包含了所请求的对象本身(表示为data data data data…)。状态行有3个字段:协议版本字段、状态码和相应状态信息。在这个例子中,状态行指示服务器正在使用http/1.1,并且一切正常(即服务器已经找到并正在发送所请求的对象)。
我们现在来看看首部行。服务器用connection: close首部行告诉客户,发送完报文后将关闭该tcp连接。date:首部行指示服务器产生并发送该响应报文的日期和时间。值得一提的是,这个时间不是指对象创建或者最后修改的时间;而是服务器从它的文件系统中检索到该对象,插入到响应报文,并发送该响应报文的时间。server:首部行指示该报文是由一台apache web服务器产生的,它类似于http请求报文中的user-agent:首部行。last-modified:首部行指示了对象创建或者最后修改的日期和时间。last-modified:首部行对既可能在本地客户也可能在网络缓存服务器上的对象缓存来说非常重要。我们将很快详细地讨论缓存服务器(也叫代理服务器)。content-length:首部行指示了被发送对象中的字节数。content-type:首部行指示了实体体中的对象是html文本。(该对象类型应该正式地由content-type:首部行而不是用文件扩展名来指示。)
看过一个例子后,我们再来查看响应报文的通用格式(如图2-9所示)。该通用格式能够与前面例子中的响应报文对应起来。我们补充说明一下状态码和它们对应的短语。状态码及其相应的短语指示了请求的结果。一些常见的状态码和相关的短语包括:
200 ok:请求成功,信息在返回的响应报文中。
301 moved permanently:请求的对象已经被永久转移了,新的url定义在响应报文的location:首部行中。客户软件将自动获取新的url。
400 bad request:一个通用差错代码,指示该请求不能被服务器理解。
404 not found:被请求的文档不在服务器上。
505 http version not supported:服务器不支持请求报文使用的http协议版本。
你想看一下真实的http响应报文吗?这正是我们高度推荐的事,而且也很容易做到。首先用telnet登录到你喜欢的web服务器上,接下来输入一个只有一行的请求报文去请求放在该服务器上的某些对象。例如,假设你看到命令提示,键入:
(在输入最后一行后连续按两次回车。)这就打开一个到主机cis.poly.edu的80端口的tcp连接,并发送一个http请求报文。你将会看到一个携带包括ross教授主页的html基本文件的响应报文。如果你只是想看一下http协议的报文行,而不是获取对象本身的话,那么可以用head代替get。最后,用/~banana/代替/~ross/,看看你得到什么类型的响应报文。
在本节中,我们讨论了http请求报文和响应报文中的一些首部行。http规范中定义了很多可以被浏览器、web服务器和web缓存服务器插入的首部行。我们只提到了全部首部行中的少数几个,在2.2.5节中我们讨论网络web缓存时还会涉及其他几个。一本可读性很强的文献是[krishnamurty 2001],它对http协议(包括它的首部行和状态码)进行了广泛讨论。
浏览器是如何决定在一个请求报文中包含哪些首部行的呢?web服务器又是如何决定在一个响应报文中包含哪些首部行呢?浏览器产生的首部行与很多因素有关,包括浏览器的类型和协议版本(例如,http/1.0浏览器将不会产生任何1.1版本的首部行)、浏览器的用户配置(如喜好的语言)、浏览器当前是否有一个缓存的但是可能超期的对象版本。web服务器的表现也类似:在产品、版本和配置上都有差异,所有这些都会影响响应报文中包含的首部行。
我们前面提到了http服务器是无状态的。这简化了服务器的设计,并且允许工程师们去开发可以同时处理数以千计的tcp连接的高性能web服务器。然而一个web站点通常希望能够识别用户,可能是因为服务器希望限制用户的访问,或者因为它希望把内容与用户身份联系起来。为此,http使用了cookie。cookie在[rfc 6265]中定义,它允许站点对用户进行跟踪。目前大多数商务web站点都使用了cookie。
如图2-10所示,cookie技术有4个组件:①在http响应报文中的一个cookie首部行;②在http请求报文中的一个cookie首部行;③在用户端系统中保留有一个cookie文件,并由用户的浏览器进行管理;④位于web站点的一个后端数据库。使用图2-10,我们通过一个典型的例子看看cookie的工作过程。假设susan总是从家中pc使用internet explorer上网,她首次与amazon.com联系。我们假定过去她已经访问过ebay站点。当请求报文到达该amazon web服务器时,该web站点将产生一个唯一识别码,并以此作为索引在它的后端数据库中产生一个表项。接下来amazon web服务器用一个包含set-cookie:首部的http响应报文对susan的浏览器进行响应,其中set-cookie:首部含有该识别码。例如,该首部行可能是
当susan的浏览器收到了该http响应报文时,它会看到该set-cookie:首部。该浏览器在它管理的特定cookie文件中添加一行,该行包含服务器的主机名和在set-cookie:首部中的识别码。值得注意的是该cookie文件已经有了用于ebay的表项,因为susan过去访问过该站点。当susan继续浏览amazon网站时,每请求一个web页面,其浏览器就会从该cookie文件中获取她对这个网站的识别码,并放到http请求报文中包括识别码的cookie首部行中。特别是,发往该amazon服务器的每个http请求报文都包括以下首部行:
在这种方式下,amazon服务器可以跟踪susan在amazon站点的活动。尽管amazon web站点不必知道susan的名字,但它确切地知道用户1678按照什么顺序、在什么时间、访问了哪些页面!amazon使用cookie来提供它的购物车服务,即amazon能够维护susan希望购买的物品列表,这样在susan结束会话时可以一起为它们付费。
如果susan再次访问amazon站点,比如说一个星期后,她的浏览器会在其请求报文中继续放入首部行cookie:1678。amazon将根据susan过去在amazon访问的网页向她推荐产品。如果susan也在amazon注册过,即提供了她的全名、电子邮件地址、邮政地址和信用卡账号,则amazon能在其数据库中包括这些信息,将她的全名与识别码相关联(以及她在过去访问过的所有页面)。这就解释了amazon和其他一些电子商务网站实现“点击购物”(one-click shopping)的道理,即当susan在后继的访问中选择购买某个物品时,她不必重新输入姓名、信用卡账号或者地址等信息了。
从上述讨论中我们看到,cookie可以用于标识一个用户。用户首次访问一个站点时,可能需要提供一个用户标识(可能是名字)。在后继会话中,浏览器向服务器传递一个cookie首部,从而向该服务器标识了用户。因此cookie可以在无状态的http之上建立一个用户会话层。例如,当用户向一个基于web的电子邮件系统(如hotmail)注册时,浏览器向服务器发送cookie信息,允许该服务器在用户与应用程序会话的过程中标识该用户。
尽管cookie常常能简化用户的因特网购物活动,但是它的使用仍具有争议,因为它们被认为是对用户隐私的一种侵害。如我们刚才所见,结合cookie和用户提供的账户信息,web站点可以知道许多有关用户的信息,并可能将这些信息卖给第三方。cookie central [cookie central 2012]包括了对cookie争论的广泛信息。
浏览器建立一个到web缓存器的tcp连接,并向web缓存器中的对象发送一个http请求。
web缓存器进行检查,看看本地是否存储了该对象副本。如果有,web缓存器就向客户浏览器用http响应报文返回该对象。
如果web缓存器中没有该对象,它就打开一个与该对象的初始服务器(如www.someschool.edu)的tcp连接。web缓存器则在这个缓存器到服务器的tcp连接上发送一个对该对象的http请求。在收到该请求后,初始服务器向该web缓存器发送具有该对象的http响应。
当web缓存器接收到该对象时,它在本地存储空间存储一份副本,并向客户的浏览器用http响应报文发送该副本(通过现有的客户浏览器和web缓存器之间的tcp连接)。
值得注意的是web缓存器是服务器同时又是客户。当它接收浏览器的请求并发回响应时,它是一个服务器。当它向初始服务器发出请求并接收响应时,它是一个客户。
web缓存器通常由isp购买并安装。例如,一所大学可能在它的校园网上安装一台缓存器,并且将所有校园网上的用户浏览器配置为指向它。或者,一个主要的住宅isp(例如aol)可能在它的网络上安装一台或多台web缓存器,并且预先配置其配套的浏览器指向这些缓存器。
在因特网上部署web缓存器有两个原因。首先,web缓存器可以大大减少对客户请求的响应时间,特别是当客户与初始服务器之间的瓶颈带宽远低于客户与web缓存器之间的瓶颈带宽时更是如此。如果在客户与web缓存器之间有一个高速连接(情况常常如此),并且如果用户所请求的对象在web缓存器上,则web缓存器可以迅速将该对象交付给用户。其次,如我们马上用例子说明的那样,web缓存器能够大大减少一个机构的接入链路到因特网的通信量。通过减少通信量,该机构(如一家公司或者一所大学)就不必急于增加带宽,因此降低了费用。此外,web缓存器能从整体上大大减低因特网上的web流量,从而改善了所有应用的性能。
图2-12 一个机构网络与因特网之间的瓶颈为了深入理解缓存器带来的好处,我们考虑在图2-12场景下的一个例子。该图显示了两个网络,即机构(内部)网络和公共因特网的一部分。机构网络是一个高速的局域网,它的一台路由器与因特网上的一台路由器通过一条15mbps的链路连接。这些初始服务器与因特网相连但位于全世界各地。假设对象的平均长度为1mb,从机构内的浏览器对这些初始服务器的平均访问速率为每秒15个请求。假设http请求报文小到可以忽略,因而不会在网络中以及接入链路(从机构内部路由器到因特网路由器)上产生什么通信量。我们还假设在图2-12中从因特网接入链路一侧的路由器转发http请求报文(在一个ip数据报中)开始,到它收到其响应报文(通常在多个ip数据报中)为止的时间平均为2秒。我们非正式地将该持续时延称为“因特网时延”。
总的响应时间,即从浏览器请求一个对象到接收到该对象为止的时间,是局域网时延、接入时延(即两台路由器之间的时延)和因特网时延之和。我们来粗略地估算一下这个时延。局域网上的流量强度(参见1.4.2节)为
(15个请求/s)×(1mb/请求)/(100mbps)=0.15
然而接入链路上的流量强度(从因特网路由器到机构路由器)为
(15个请求/s)×(1mb/请求)/(15mbps)=1
局域网上强度为0.15的通信量最多导致数十毫秒的时延,因此我们可以忽略局域网时延。然而,如在1.4.2节讨论的那样,如果流量强度接近1(就像在图2-12中接入链路的情况那样),链路上的时延会变得非常大并且无限增长。因此,满足请求的平均响应时间将在分钟的量级上。显然,必须想办法来改进时间响应特性。
一个可能的解决办法就是增加接入链路的速率,如从15mbps增加到100mbps。这可以将接入链路上的流量强度减少到0.15,这样一来,两台路由器之间的链路时延也可以忽略了。这时,总的响应时间将大约为2秒钟,即为因特网时延。但这种解决方案也意味着该机构必须将它的接入链路由15mbps升级为100mbps,这是一种代价很高的方案。
现在来考虑另一种解决方案,即不升级链路带宽而是在机构网络中安装一个web缓存器。这种解决方案如图2-13所示。实践中的命中率(即由一个缓存器所满足的请求的比率)通常在0.2~0.7之间。为了便于阐述,我们假设该机构的缓存命中率为0.4。因为客户和缓存连接在一个相同的高速局域网上,这样40%的请求将几乎立即会由缓存器得到响应,时延约在10ms以内。然而,剩下的60%的请求仍然要由初始服务器来满足。但是只有60%的被请求对象通过接入链路,在接入链路上的流量强度从1.0减小到0.6。一般而言,在15mbps链路上,当流量强度小于0.8时对应的时延较小,约为几十毫秒。这个时延与2秒因特网时延相比是微不足道的。考虑这些之后,平均时延因此为0.4×(0.010秒)+0.6×(2.01秒) 图2-13 为机构网络添加一台缓存器这略大于1.2秒。因此,第二种解决方案提供的响应时延甚至比第一种解决方案更低,也不需要该机构升级它到因特网的链路。该机构理所当然地要购买和安装web缓存器。除此之外其成本较低,很多缓存器使用了运行在廉价pc上的公共域软件。
通过使用内容分发网络(content distribution network,cdn),web缓存器正在因特网中发挥着越来越重要的作用。cdn公司在因特网上安装了许多地理上分散的缓存器,因而使大量流量实现了本地化。有多个共享的cdn(例如akamai和limelight)和专用的cdn(例如谷歌和微软)。我们将在第7章中更为详细地讨论cdn。
尽管高速缓存能减少用户感受到的响应时间,但也引入了一个新的问题,即存放在缓存器中的对象副本可能是陈旧的。换句话说,保存在服务器中的对象自该副本缓存在客户上以后可能已经被修改了。幸运的是,http协议有一种机制,允许缓存器证实它的对象是最新的。这种机制就是条件get(conditional get)方法。如果:①请求报文使用get方法;并且②请求报文中包含一个“if-modified-since:”首部行。那么,这个http请求报文就是一个条件get请求报文。
为了说明get方法的操作方式,我们看一个例子。首先,一个代理缓存器(proxy cache)代表一个请求浏览器,向某web服务器发送一个请求报文:
其次,该web服务器向缓存器发送具有被请求的对象的响应报文:
该缓存器在将对象转发到请求的浏览器的同时,也在本地缓存了该对象。重要的是,缓存器在存储该对象时也存储了最后修改日期。第三,一个星期后,另一个用户经过该缓存器请求同一个对象,该对象仍在这个缓存器中。由于在过去的一个星期中位于web服务器上的该对象可能已经被修改了,该缓存器通过发送一个条件get执行最新检查。具体说来,该缓存器发送:
值得注意的是if-modified-since:首部行的值正好等于一星期前服务器发送的响应报文中的last-modified:首部行的值。该条件get报文告诉服务器,仅当自指定日期之后该对象被修改过,才发送该对象。假设该对象自2011年9月7日09:23:24后没有被修改。接下来的第四步,web服务器向该缓存器发送一个响应报文:
我们看到,作为对该条件get方法的响应,该web服务器仍发送一个响应报文,但并没有在该响应报文中包含所请求的对象。包含该对象只会浪费带宽,并增加用户感受到的响应时间,特别是如果该对象很大的时候更是如此。值得注意的是在最后的响应报文中,状态行中为304 not modified,它告诉缓存器可以使用该对象,能向请求的浏览器转发它(该代理缓存器)缓存的该对象副本。
我们现在完成了对http的讨论,这是我们详细学习的第一个因特网协议(应用层协议)。我们已经学习了http报文的格式,学习了当发送和接收这些报文时web客户和服务器所采取的动作。我们还学习了一点web应用程序基础设施,包括缓存、cookie和后端数据库,所有这些都以某种方式与http协议有关。