大概每家互联网公司在笔试招人时都会出这样一道题:写出 从你在浏览器中输入一个URL整个页面加载完整结束 这个过程发生了哪些事情,答案越详细越好。我在网上找到一篇不错的英文博客说明这一过程,并把它翻译了下来(有不理解的地方可以查看原文或留言):
原文链接:URL原文
以下是译文内容:
作为一个程序员,你肯定对 web应用是如何工作的以及像HTTP,HTML,web服务器,处理请求器等等包括什么技术有自己独到的看法。
在这篇文章里,我们将会深入了解访问一个URL依次会发生什么。
1.在浏览器中输入一个URL
从这里开始:
在ie中输入Facebook.com2.浏览器对这个域名的IP地址进行查询
通过DNS服务器查找第一步是通过DNS找到这个域名的IP地址,DNS查找的顺序如下所示:
1.浏览器缓存 — 浏览器会缓存DNS记录一段时间。但操作系统不会告诉浏览器每条DNS记录的存活时间,所以浏览器会将其缓存一个固定的时间(大多浏览器会缓存记录2—30分钟)。
2.操作系统缓存 — 如果浏览器缓存中没有所需的缓存记录,浏览器会进行一个系统调用(Windows中的gethostbyname).操作系统拥有自己的缓存。
3.路由器缓存 — (若前两个缓存都没有所需的记录),请求会继续发送到拥有DNS缓存的路由器上。
4.ISP DNS缓存 — 下个搜索的地方是ISP的DNS服务器(服务器里有缓存).
5.递归搜索 — 若前四个都没找到,请求接下来会到ISP的DNS服务器中,进行一个递归搜索。搜索的顺序是从根名称服务器 到 .com顶级名称服务器(nameserver)再到 Facebook的名称服务器。一般情况下,DNS服务器会缓存具有.com名称的名称服务器,因此在根名称服务器寻找没有意义。
下图为DNS递归查询示意图:
DNS递归查询示意图使用DNS有个麻烦:像wikipedia.com或者facebook.com这样的(访问量极大的)域名似乎只能映射到一个IP地址。幸运的是,有一些方法可以减轻瓶颈:
循环DNS(Round-robin DNS) 定义:DNS查找时返回多个IP地址。例如:Facebook.com实际上映射到4个ip地址(分散访问压力)。
负载均衡器(Load-balancer). 这是一种可以监听特定IP地址 并将 请求转发到别的服务器上的硬件,很多主站点都会使用性能很高的负载均衡器。
地理DNS(Geographic DNS). 它根据用户的地理位置把域名映射到不同的IP地址上来提高拓展性。这个方法使不同的服务器不必更新共享状态,非常适合托管静态内容。
Anycast. 这是一种可以把单个IP地址映射到多个物理服务器的路由技术。不过这项技术与TCP协议不兼容,所以在这样的情况下使用较少。
大多数DNS服务器使用Anycast来实现 DNS查找过程中 的高可用和低延迟。
3.浏览器向Web服务器发送HTTP请求
GET请求由于Facebook主页是动态页面(动态页面到期速度非常快),所以它的主页不会被缓存在浏览器中。
因此,浏览器会把一下请求发送给Facebook服务器:
GET http://facebook.com/HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml,[...]
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64;[...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: facebook.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[…]
以下是对这些请求的说明:
GET请求命名URL并做提取:“http://facebook.com/”。浏览器验证自身(User-Agent头部)并声明它将要接受什么类型的响应(Accept和Accept-Encoding头部的gzip, deflate)。Connection头部请求保持TCP连接以保证接受更多的请求。
此请求还包括浏览器对这个域名保存的cookies。Cookies是追踪网站的不同页面的请求状态的键值对。在这里,Cookies存储了已登录用户的用户名、服务器分配给用户的密码,用户设置等等。Cookies存储在本地客户端的文本中,并随着每个请求发送到服务器端。
有很多工具可以查看HTTP请求和回应。我最喜欢的工具是fiddler,也有很多其他工具(如FireBug)可以帮助我们优化一个网站。
除了GET请求,你可能熟悉的另一种请求是POST请求,这种请求通常用于提交表单。GET请求通过URL发送其参数(例如http://robozzle.com/puzzle.aspx?id=85)。而POST请求通过他头部下方的请求体发送其参数。
URL“http://facebook.com/” 尾部的 斜杠(/) 很重要。在前面的情况下(“http://facebook.com/”),浏览器可以安全地添加一个斜杠。而像“http://example.com/folderOrFile”这样的URL,浏览器无法在尾部自动添加斜杠,因为浏览器不知道folderOrFile是文件夹还是文件。在这样的情况下,浏览器不会在URL尾部添加“/”。此时服务器将对请求进行一个重定向,会导致不必要的结果。
4.facebook服务器对请求进行永久重定向(301)
因为URL有问题以下是Facebook服务器返回给浏览器的响应内容:
HTTP/1.1 301 Moved Permanently
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Location:http://www.facebook.com/
P3P: CP="DSP LAW"
Pragma: no-cache
Set-Cookie: made_write_conn=deleted; expires=Thu, 12-Feb-2009 05:09:50 GMT;
path=/; domain=.facebook.com; httponly
Content-Type: text/html; charset=utf-8
X-Cnection: close
Date: Fri, 12 Feb 2010 05:09:51 GMT
Content-Length: 0
服务器返回一个301永久重定向响应,告诉浏览器跳转到“http://www.facebook.com/”而不是“http://facebook.com/”。
对于为何服务器坚持重定向而不是响应用户想查看的网页有些有趣的原因:
首先与搜索引擎排名有关,如果两个URL指向同一个页面(如http://www.igoro.com/和http://igoro.com/),搜索引擎可能会认为这是两个不同的站点,由于这两个网站的入站链路较少因此排名较低。搜索引擎理解永久重定向(301),会把两个来源的入站链接组成为一个排名。
此外,相同内容用多个URL表示不是 缓存友好型(cache-friendly)。当一段内容有多个名称时,可能会在缓存中缓存多次。
5.浏览器进行重定向
重定向浏览器知道“http://www.facebook.com/”是要访问的正确的URL后,会发出另一个GET请求。
GET http://www.facebook.com/HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml,[...]
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64;[...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Cookie: lsd=XW[...]; c_user=21[...]; x-referer=[...]
Host:www.facebook.com
报文内容与上个请求一样。
6.服务器处理请求
...服务器收到GET请求后,进行处理并发送响应。
这看似一个简单的任务,但这个过程里发生了很多有趣的事—这个过程在个人博客网站上都会发生,更不用说像facebook这样大规模的拓展网站了。
Web服务器软件
Web服务软件(如:IIS,Apache)收到HTTP请求后会决定用哪个处理程序去处理这个请求。处理程序(用ASP.NET,PHP,Ruby等语言写成)处理请求并生成响应的HTML。
在最简单的情况下,请求处理器可以被存储到一个可以反映URL结构的文件层次中(如:http://example.com/folder1/page1.aspx的URL存储在文件/httpdocs/folder1/page1.aspx中)。你也可以配置服务器软件,使得URL可以手动地存储到处理请求器中,这样的话,page1.aspx的公共URL可以是http://example.com/folder1/page1
处理请求器(Request handler)
处理请求器读取请求,请求参数和cookies。它可能会读取并更新存储在服务器上的一些数据。然后,请求处理器会生成一个HTML文档。
每个动态站点都面临着一个有趣的难题:如何存储数据。较小的站点通常只用一个数据库存储它们的数据,但是对于 存储着大量数据的网站 或 访问量很大的网站来说,需要多台机器分割数据库。分割方法包括:数据分片(基于主键把多个数据库的表进行分割),复制 和 减少使用 一致性的简化数据库。
让数据更新成本变低的一种方法是把一些数据延迟到批处理进行更新。例如:Facebook需要实时更新新闻,而有些如“您可能认识的人”这样的功能的数据只需要每晚进行更新(这是我的猜测,实际上的更新机制我也不清楚)。批处理更新使一些不太重要的数据进行统一,从而使数据更新更快更简单。
7.服务器返回HTML响应内容
以下是服务器生成并返回的响应:
HTTP /1.1 200 OK
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
P3P: CP="DSP LAW"
Pragma: no-cache
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
X-Cnection: close
Transfer-Encoding: chunked
Date: Fri, 12 Feb 2010 09:05:55 GMT
2b3
��������T�n�@����[...]
响应内容大小为36kb,但他们大多数被我修剪掉了。
Content-Encoding头告诉浏览器相应内容用gzip算法进行压缩。解压缩后,你将会看到你期望的HTML内容:
复制粘贴时有问题,这看来是简书的一个BUG吧; )除了压缩,头部还会指定 是否缓冲页面,如何缓存页面,设置cookies(这个响应内容中无),私有信息等。
8.浏览器开始渲染HTML
在接收到整个HTML文档前,浏览器就会开始渲染站点:
静态内容9.浏览器发送嵌入HTML对象的请求
当浏览器渲染HTML时,它会注意到需要从其他URL获取的标签(tag)。浏览器会发送GET请求来获取这些文件。
以下是我访问facebook需要的几个URL:
Images
http://static.ak.fbcdn.net/rsrc.php/z12E0/hash/8q2anwu7.gif
http://static.ak.fbcdn.net/rsrc.php/zBS5C/hash/7hwy7at6.gif
…
CSS style sheets
http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.css
http://static.ak.fbcdn.net/rsrc.php/zANE1/hash/cvtutcee.css
…
JavaScript files
http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.js
http://static.ak.fbcdn.net/rsrc.php/z6R9L/hash/cq2lgbs8.js
…
上面每个URL都会经历这个HTML构建时的过程。如浏览器在DNS里查找域名,向URL发送请求,重定向等。
静态文件(与动态页面不同)允许浏览器缓存他们。有些文件可能会被存储到缓存中而不需要服务器提供。浏览器知道缓存一个特定的文件需要多长时间,因为响应文件包括一个Expires头。此外,每个响应可能还包括一个ETag头部(类似于版本号)—如果浏览器看到本地文件中已经有这个版本的ETag了,就会停止传输。
你能猜到这个URL中的“fbcdn.net”代表什么吗?一种看法是认为它意思是“Facebook内容传送网络”。Facebook使用内容传送网络(CDN)来分发静态内容—图像,样式表和JavaScript文件。因此,这些文件会被复制传输到全球范围内很多服务器中。
静态内容大多占一个站点的大部分带宽,而且能够通过CDN被复制。通常,网站会使用第三方的CDN提供商而不是自己运行CDN。例如,Facebook的静态文件就是由全球最大的CDN提供商Akamai的CDN设备里运行的。
作为一个演示,当你ping static.ak.fbcdn.net时,你会收到akamai,net服务器的响应。有趣的是,重复ping这个URL,可能会收到来自不同服务器的响应,这表明后台使用了负载均衡(译者ping了10次都是一个ip)。
10.浏览器进一步发送AJAX请求
恭喜你能看到这里在Web2.0的时代,在页面已经渲染完成的情况下客户端依然可以和服务器进行通信。
例如:Facebook chat会持续更新你在线好友列表。要达到这一点,浏览器执行的JS脚本需要向服务器发送异步请求。异步请求是代码构建的GET、POST请求,转向特殊的URL。在Facebook的例子里,客户端发送一个POST请求到http://www.facebook.com/ajax/chat/buddy_list.php来获取你在线好友的列表。
这种模式有时被称为“AJAX”。代表“Asynchronous JavaScript And XML”,服务器必须用XML的响应格式(没有特殊的理由)。例如:Facebook返回JavaScript代码段来响应异步请求。
除此之外,fiddler工具可以通过浏览器查看异步请求。实际上,你不仅可以观察请求,还可以修改后重新发送它们,这样很容易欺骗AJAX请求。
Facebook chat提供了一个AJAX的有趣例子:把数据从服务器推送到客户端。由于HTTP协议是一种 请求-响应 协议,chat 服务器无法推送新信息到客户端。相反,客户端必须每隔一段时间就访问服务器看是否有新消息到达。
长时间轮询(long polling)是一种减少这些场景中服务器负载的技术。当服务器没有新信息时,它将不会发送响应。而且,若接收客户端的消息时超时了,服务器会发现这条请求并返回消息。
结论
希望这篇文章可以使你更好理解不同网页(web piece)如何协同工作。
网友评论