【翻译】当你访问一个URL的时候到底发生了什么

大体讲述了「当你访问一个URL的时候到底发生了什么」。这篇博客仅仅提供了一个大局观,在输入URL到获得页面中间还有很多未提到的细节,可以自己再查阅资料。我觉得原文写得蛮不错的,所以就翻译过来啦。

原文链接

What really happens when you navigate to a URL

当你访问一个URL的时候到底发生了什么

作为一个软件开发者,你应该对于web应用程序到底是怎么工作的,以及这中间包含着什么样的技术有着非常清晰的理解🤔️:浏览器、HTTP、HTML、web服务器、请求处理等等。

1. 当你在浏览器中输入一个URL

一切都是源于这里:

image-20190323162354836

2. 浏览器会寻找这个域名的IP地址

image-20190323162439306

在这段网上冲浪的过程中,第一步就是需要找到想要访问的域名的ip地址。DNS的查询是这样的:

  • 浏览器缓存 — 浏览器会缓存DNS记录一段时间。有趣的是,操作系统没有告知浏览器每条DNS记录的time-to-live,因此浏览器会以一个固定的时间长度来缓存这些DNS记录(每个浏览器之间不同,一般是2-30分钟)

  • 操作系统缓存 — 如果浏览器缓存没有找到,那么浏览器会调用系统查询。(Windows中是gethostbyname)。操作系统有他自己的缓存。

    译者注:应该就是所谓的硬盘缓存。

  • 路由器缓存 — 这个查询会继续到你的路由器上,它也有自己的DNS缓存哦😯

  • ISP DNS缓存 — 下一个要查询的就是服务提供商(电信网、移动网等)的DNS服务器。

  • 递归搜索 — 您的ISP的DNS服务器开始递归搜索,从根域名服务器,通过.com顶级域名服务器,到Facebook的名称服务器。通常,DNS服务器(ISP的)将在缓存中具有.com名称服务器的名称,因此不需要命中根名称服务器。

这是一张递归搜索的示意图。

image-20190323163332014

译者注:如果所有缓存都没有命中,就会去查找根服务器,如果域名是.org,就会从根域名服务器拿到org顶级域名服务器的ip地址;然后去org顶级域名服务器那里去询问wikipedia.org服务器的位置,拿到ip地址之后再去找wikipedia.org服务器,得到最终的ip地址。

关于DNS的一个令人担忧的事情是像wikipedia.org或facebook.com这样的整个域似乎映射到单个IP地址。幸运的是,有一些方法可以缓解瓶颈:

  • 循环DNS 是一种解决方案,其中DNS查找返回多个IP地址,而不仅仅是一个。例如,facebook.com实际上映射到四个IP地址。

  • 负载均衡器 是侦听特定IP地址并将请求转发到其他服务器的硬件。主要站点通常使用昂贵的高性能负载平衡器。

  • 地理DNS 通过将域名映射到不同的IP地址来提高可扩展性,具体取决于客户端的地理位置。这非常适合托管静态内容,以便不同的服务器不必更新共享状态。
  • Anycast 是一种路由技术,其中单个IP地址映射到多个物理服务器。不幸的是,anycast不适合TCP,在这种情况下很少使用。

大多数DNS服务器本身使用anycast来实现DNS查找的高可用性和低延迟。

3. 浏览器向web服务器发送HTTP请求

image-20190323171533154

Facebook的首页不可能从浏览器缓存中读取出来,因为动态网页会很快就过期或者立刻就过期(通过将expiry date设为过去的日期)

所以浏览器需要向Facebook服务器发送以下的报文请求:

image-20190323171951438

这个GET请求指明了要获取的URL:http://facebook.com。浏览器标识自己(User-Agent标识头),并说明它将接受哪些类型的响应(AcceptAccept-Encoding标识头)。Connection标头要求服务器保持TCP连接打开以进一步请求。

该请求还包含浏览器为此域提供的cookie。您可能已经知道,Cookie是键值对,用于跟踪不同页面请求之间的网站状态。因此,cookie存储登录用户的名称,服务器分配给用户的密码,用户的一些设置等。这些cookie将存储在客户端的文本文件中,并发送到每个请求的服务器。

有很多工具可以让您来查看原是的HTTP请求和响应。我最喜欢的查看网页流量的工具是fiddler,但还有很多其他的工具(例如FireBug)。这些工具在你优化网页的时候会非常有帮助。

除了GET请求,你应该也非常熟悉另一种POST请求,它通常用来提交表单。一个GET请求通过URL(例如http://robozzle.com/puzzle.aspx**?id=85**)来传递参数。而POST请求通过请求体来传递参数。

URL“http://facebook.com/” 中的尾部斜杠非常重要。在这种情况下,浏览器可以安全地添加斜杠。对于”http://example.com/folderOrFile"这种网址,浏览器不能自动添加一个斜杠,因为`folderOrFile`不确定是一个文件夹或者是一个文件。在这种情况下,浏览器将访问没有斜杠的URL,服务器将响应重定向,从而导致不必要的往返。

4. Facebook服务器返回了一个永久重定向(301)的响应

image-20190323173124807

这是facebook服务器返回给浏览器请求的响应。

image-20190323173155139

服务器给出了一个301 Moved Permanently的响应来告知浏览器去访问http://www.facebook.com而不是http://facebook.com/

为什么浏览器坚持要给出一个重定向而不是直接返回用户想要看到的web页面呢?这中间有非常有趣的原因。

一个原因与搜索引擎排名有关。假设如果现在有两条URL指向一个页面(例如http://www.igoro.com/和http://igoro.com/,搜索引擎会将它们认做两个不同的网页,每个都会有较少的传入链接,因此排名都会较低。但是搜索引擎能理解永久性重定向(301),这样它就会把来自两个源的传入链接组合成单个排名。

同时,相同内容的不同URL不是缓存友好的。当一段内容具有多个名称时,它可能会在缓存中多次出现。

5. 浏览器遵循重定向

image-20190323174526175

此时浏览器知道了http://www.facebook.com是要访问的正确URL。所以它会再次发出另一个GET请求。

image-20190323174608992

请求头的含义与第一次请求是一样的。

6. 服务器“处理”请求

image-20190323174840116

服务器收到GET请求的时候会处理它,并返回一个响应。

这似乎是一项简单的任务,但实际上这里发生了许多有趣的事情 - 即使是在像我的博客这样简单的网站上,更不用说在像facebook这样的大规模可扩展的网站上。

  • web服务器软件

    web服务器软件(例如ISS或者Apache)接收HTTP请求并决定应该执行哪个请求处理程序来处理该请求。请求处理程序是一个程序(在ASP.NETPHPRuby…中),它读取请求并生成响应的HTML。在最简单的情况下,请求处理程序可以存储在文件层次结构中,其结构镜像URL结构。因此例如http://example.com/folder1/page1.aspx URL将映射到文件/ httpdocs / folder1 / page1的.aspx。还可以配置Web服务器软件,以便将URL手动映射到请求处理程序,因此page1.aspx的公共URL可以是http://example.com/folder1/page1。

  • 请求处理

    请求处理程序读取请求,参数和cookie。它将读取并可能更新存储在服务器上的一些数据。然后,请求处理程序将生成HTML响应。

每个动态网页都会遇到的有趣困难时如何存储数据。小一点的网站一般会有一个独立的SQL数据库来存储数据,但是如果是存储了大量数据或者有很多访客的网站必须找到一种在多台机器上拆分数据库的方法。解决方案包括分片(基于主键在多个数据库中拆分表),复制和使用具有弱化一致性语义的简化数据库。

保持数据更新便宜的一种技术是将一些工作推迟到批处理作业。例如,Facebook必须及时更新新闻源,但支持“你可能知道的人”功能的数据可能只需要每晚更新(我猜,我实际上并不知道他们如何实现这个功能)。批处理作业更新导致一些不太重要的数据过时,但可以使数据更新更快更简单。

7. 服务器返回一个HTML响应

image-20190323205521637

以下是服务器生成并发送回来的响应:

image-20190323205553332

整个响应是36 kB,其中大部分是在我修剪的结尾处的字节二进制对象中。

Content-Encoding响应头告诉浏览器响应体使用gzip算法压缩。当解压缩这个二进制对象,你会得到你期待的HTML!😊

image-20190323205814996

除了压缩以外,标识头还指定是否以及如何缓存页面,是否要设置cookie(此响应中没有),隐私信息等等。

请注意将Content-Type设置为text / html的标头。标题指示浏览器将响应内容呈现为HTML,而不是将其作为文件下载。浏览器将使用标头来决定如何解释响应,但也会考虑其他因素,例如URL的扩展名。

8. 浏览器开始渲染HTML

甚至在浏览器收到整个HTML文档之前,它就开始渲染网站了。

image-20190323212621486

9. 浏览器发送对HTML中嵌入的对象的请求

image-20190323212846488

当浏览器呈现HTML时,它会注意到需要获取其他URL的标记。浏览器将发送GET请求以检索每个文件。

以下是我访问facebook.com时检索到的一些网址:

这些URL每个都将经历与HTML页面类似的过程。因此,浏览器将在DNS中查找域名,向URL发送请求,遵循重定向等。

但是,静态文件(与动态网页不同)允许浏览器缓存他们。某些文件可以直接从缓存中提供,而根本不联系服务器。浏览器知道缓存特定文件的时间长度,因此返回文件的响应包含Expires头(译者注:目前与缓存有关的响应头有:Expires、Cache-control、Last-Modified / If-Modified-Since、Etag / If-None-Match)此外,每个响应还可能包含一个类似于版本号的ETag标识头,如果浏览器看到它已经拥有的文件版本的ETag,它可以立即停止传输。

你能猜出URL中的“fbcdn.net”代表什么吗?一个安全的猜测是它意味着“Facebook内容分发网络”。 Facebook使用内容分发网络(CDN)来分发静态内容 - 图像,样式表和JavaScript文件。因此,这些文件将被复制到全球的许多机器上。

静态内容通常代表站点的大部分带宽,并且可以通过CDN轻松复制。通常,站点将使用第三方CDN提供商,而不是自己运行CDN。例如,Facebook的静态文件由最大的CDN提供商Akamai托管。

作为演示,当您尝试ping static.ak.fbcdn.net时,您将收到来自akamai.net服务器的响应。另外,有趣的是,如果您多次ping URL,可能会收到来自不同服务器的响应,这表明在幕后发生的负载平衡。

10. 浏览器发送进一步的异步(AJAX)请求

image-20190323215315324

本着Web 2.0的精神,即使在呈现页面之后,客户端仍继续与服务器通信。

例如,Facebook聊天将继续更新您登录的朋友列表。要更新已登录朋友的列表,浏览器中执行的JavaScript必须向服务器发送异步请求。异步请求是以编程方式构造的GET或POST请求,该请求转到特殊URL。在Facebook示例中,客户端向 http://www.facebook.com/ajax/chat/buddy_list.php 发送POST请求以获取您在线的朋友列表。

这种模式有时被称为“AJAX”,它代表“异步JavaScript和XML”,即使服务器必须将响应格式化为XML也没有特别的原因。例如,Facebook返回JavaScript代码片段以响应异步请求。

除此之外,fiddler工具还允许您查看浏览器发送的异步请求。实际上,您不仅可以被动地观察请求,还可以修改和重新发送它们。事实上,这种易于“欺骗”的AJAX请求会给使用记分板的在线游戏的开发者带来很多麻烦。 (显然,请不要以这种方式作弊。)

Facebook聊天提供了一个AJAX有趣问题的例子:将数据从服务器推送到客户端。由于HTTP是请求 - 响应协议,因此聊天服务器无法将新消息推送到客户端。相反,客户端必须每隔几秒轮询服务器以查看是否有任何新消息到达。

在这些类型的场景中,长轮询是一种减少服务器负载的有趣技术。如果服务器在轮询时没有任何新消息,则它不会发回响应。并且,如果在超时期限内收到此客户端的消息,服务器将找到未完成的请求并返回带有响应的消息。

总结

希望这可以让您更好地了解不同的Web部件如何协同工作。