从输入URL到页面加载发生了什么?
1, 在浏览器中输入URL
2, 浏览器通过域名去找到对应的IP
- 浏览器缓存 – 浏览器时不时的会缓存DNS记录. 有意思的是, OS并没有明确指明浏览器每条记录的生命周期是多长, 所以浏览器定期的缓存DNS记录(大概2-30分钟不等) chrome://net-internals/#dns.
- 系统缓存 – 如果缓存中没有,就去调用 gethostbyname 库函数(操作系统不同函数也不同)进行查询。函数在试图进行DNS解析之前首先检查域名是否在本地 Hosts 里,Hosts 的位置 不同的操作系统有所不同.
- 路由缓存 – 如果 gethostbyname 没有这个域名的缓存记录,也没有在 hosts 里找到,它将会向 DNS 服务器发送一条 DNS 查询请求。DNS 服务器是由网络通信栈提供的,通常是本地路由器或者 ISP 的缓存 DNS 服务器.
- ISP DNS 缓存 – 查询本地网络提供商的DNS服务器.
- 递归查找 – Your ISP’s DNS server begins a recursive search, from the root nameserver, through the .com top-level nameserver, to Facebook’s nameserver. Normally, the DNS server will have names of the .com nameservers in cache, and so a hit to the root nameserver will not be necessary.
3, 浏览器向服务器发送一个HTTP请求
由于facebook的页面是动态的,所以浏览器不会从缓存里读页面的内容,浏览器会向服务器发送HTTP请求。
这个请求看上去可能是这样的
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 header)来表明自己是谁, 以及自己接受什么样类型的响应 (Accept and Accept-Encoding headers). Connection header告诉服务器不要关闭TCP连接以便为后来请求所重用.
请求中还会带上cookie,cookie就是一些键值对,可以用来保持不同页面请求间的状态。因此cookie可以储存登录的用户的名称,服务端为用户分配的唯一密码以及一些用户设置信息等。cookie是存在客户端的,每次客户端向服务器发送请求都会带上cookie。
In addition to GET requests, another type of requests that you may be familiar with is a POST request, typically used to submit forms. A GET request sends its parameters via the URL (e.g.:http://robozzle.com/puzzle.aspx?id=85). A POST request sends its parameters in the request body, just under the headers.
The trailing slash in the URL “http://facebook.com/”is important. In this case, the browser can safely add the slash. For URLs of the formhttp://example.com/folderOrFile, the browser cannot automatically add a slash, because it is not clear whether folderOrFile is a folder or a file. In such cases, the browser will visit the URL without the slash, and the server will respond with a redirect, resulting in an unnecessary roundtrip.
4, 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
这告诉浏览器你应该访问"[http://www.facebook.com/"而不是"http://facebook.com/](http://www.facebook.com/"而不是"http://facebook.com/)"
There are interesting reasons why the server insists on the redirect instead of immediately responding with the web page that the user wants to see. One reason has to do with search engine rankings. See, if there are two URLs for the same page, sayhttp://www.igoro.com/andhttp://igoro.com/, search engine may consider them to be two different sites, each with fewer incoming links and thus a lower ranking. Search engines understand permanent redirects (301), and will combine the incoming links from both sources into a single ranking. Also, multiple URLs for the same content are not cache-friendly. When a piece of content has multiple names, it will potentially appear multiple times in caches.
5, 浏览器跟随重定向
浏览器向"[http://www.facebook.com/"发送请求](http://www.facebook.com/"发送请求)
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, 浏览器开始处理请求
浏览器开始处理请求并返回响应。
这个过程看上去很简单,但实际上却是大有文章的。
Web server software
The web server software (e.g., IIS or Apache) receives the HTTP request and decides which request handler should be executed to handle this request. A request handler is a program (in ASP.NET, PHP, Ruby, …) that reads the request and generates the HTML for the response. In the simplest case, the request handlers can be stored in a file hierarchy whose structure mirrors the URL structure, and so for examplehttp://example.com/folder1/page1.aspxURL will map to file /httpdocs/folder1/page1.aspx. The web server software can also be configured so that URLs are manually mapped to request handlers, and so the public URL of page1.aspx could behttp://example.com/folder1/page1.
Request handler
The request handler reads the request, its parameters, and cookies. It will read and possibly update some data stored on the server. Then, the request handler will generate a HTML response.
One interesting difficulty that every dynamic website faces is how to store data. Smaller sites will often have a single SQL database to store their data, but sites that store a large amount of data and/or have many visitors have to find a way to split the database across multiple machines. Solutions include sharding (splitting up a table across multiple databases based on the primary key), replication, and usage of simplified databases with weakened consistency semantics.
One technique to keep data updates cheap is to defer some of the work to a batch job. For example, Facebook has to update the newsfeed in a timely fashion, but the data backing the “People you may know” feature may only need to be updated nightly (my guess, I don’t actually know how they implement this feature). Batch job updates result in staleness of some less important data, but can make data updates much faster and simpler.
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 header告诉浏览器响应的body是被gzip给压缩过的,将body解压之后,原本的HTML就显示出来了。
<
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en" id="facebook" class=" no_js"
>
<
head
>
<
meta http-equiv="Content-type" content="text/html; charset=utf-8" /
>
<
meta http-equiv="Content-language" content="en" /
>
...
除了解压方式之外,headers还会告诉浏览器如何缓存页面等,总之headers很重要,这个后面会详细讲解。
Notice the header that sets Content-Type to text/html. The header instructs the browser to render the response content as HTML, instead of say downloading it as a file. The browser will use the header to decide how to interpret the response, but will consider other factors as well, such as the extension of the URL.
8, 浏览器开始渲染HTML
9, 浏览器发送请求去获取HTML中的一些内嵌对象
浏览器会开始获取页面上的一些引用资源,比如
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
…
每一个请求都是跟上面的GET请求差不多,都要经过dns解析等一系列过程。
不过静态文件是可以被浏览器缓存的,所以有一些静态文件浏览器可能直接从缓存里读取,而不需要去服务器端读。可以通过chrome://cache/来查看浏览器缓存。后面会详细讲到浏览器的缓存策略。
Can you guess what “fbcdn.net” in the URLs stands for? A safe bet is that it means “Facebook content delivery network”. Facebook uses a content delivery network (CDN) to distribute static content – images, style sheets, and JavaScript files. So, the files will be copied to many machines across the globe.
Static content often represents the bulk of the bandwidth of a site, and can be easily replicated across a CDN. Often, sites will use a third-party CDN provider, instead of operating a CND themselves. For example, Facebook’s static files are hosted by Akamai, the largest CDN provider.
As a demonstration, when you try to ping static.ak.fbcdn.net, you will get a response from an akamai.net server. Also, interestingly, if you ping the URL a couple of times, may get responses from different servers, which demonstrates the load-balancing that happens behind the scenes.
10, 浏览器发送异步的AJAX请求
In the spirit of Web 2.0, the client continues to communicate with the server even after the page is rendered. For example, Facebook chat will continue to update the list of your logged in friends as they come and go. To update the list of your logged-in friends, the JavaScript executing in your browser has to send an asynchronous request to the server. The asynchronous request is a programmatically constructed GET or POST request that goes to a special URL. In the Facebook example, the client sends a POST request tohttp://www.facebook.com/ajax/chat/buddy_list.phpto fetch the list of your friends who are online.
This pattern is sometimes referred to as “AJAX”, which stands for “Asynchronous JavaScript And XML”, even though there is no particular reason why the server has to format the response as XML. For example, Facebook returns snippets of JavaScript code in response to asynchronous requests.
Among other things, the fiddler tool lets you view the asynchronous requests sent by your browser. In fact, not only you can observe the requests passively, but you can also modify and resend them. The fact that it is this easy to “spoof” AJAX requests causes a lot of grief to developers of online games with scoreboards. (Obviously, please don’t cheat that way.)
Facebook chat provides an example of an interesting problem with AJAX: pushing data from server to client. Since HTTP is a request-response protocol, the chat server cannot push new messages to the client. Instead, the client has to poll the server every few seconds to see if any new messages arrived.
Long polling is an interesting technique to decrease the load on the server in these types of scenarios. If the server does not have any new messages when polled, it simply does not send a response back. And, if a message for this client is received within the timeout period, the server will find the outstanding request and return the message with the response
引用
HTTP协议概述
HTTP是一种能够获取如HTML这样网络资源的协议。它是Web上数据交换的基础,是一种client-server协议,也就是说请求通常是由像浏览器这样的接受方发起的。一个完整的web文档是由不同的子文档重新组合而成的,像是文本、布局描述、图片、视频、脚本等等。
客户端和服务端通过交换各自的消息(与数据流正好相反)来进行交互。通常由像浏览器这样的客户端发出的消息叫做 requests,那么被服务端回应的消息就叫做 responses。
HTTP被设计于上20世纪90年代初期,是一种可扩展性的协议。它是应用层的协议,虽然理论上它可以通过任何可靠的传输协议来发送,但是它还是通过TCP,或者是TLS-加密的TCP连接来发送。因为它很好的扩展性,时至今日它不仅被用来传输超文本文档,还用来传输图片、视频或者向服务器发送如HTML表单这样的信息。HTTP还可以根据网页需求,来获取部分web文档的内容来更新网页。
HTTP协议的参与者
HTTP是一个client-server协议:请求通过一个实体被发出,实体也就是用户代理。大多数情况下,这个用户代理都是指浏览器,当然它也可能是任何东西,比如一个爬取网页来生成和维护搜索引擎索引的机器。
每一个发送到服务器的请求,都会被服务器处理并且返回一个消息,也就是response。在client与server之间,还有许许多多的被称为proxies的实体,他们的作用与表现各不相同,比如有些是网关,还有些是caches等。
实际上,在一个浏览器和处理请求的服务器之间,还有计算机、路由器、调制解调器等等许多实体。由于Web的层次设计,那些在网络层和传输层都不可见了。HTTP是在最上层应用层中的,虽然下面的层次对分析网络问题非常重要,但是对HTTP的描述来说,这些大多数都是不相关的。
扩展阅读OSI Model
客户端: user-agent
严格意义来说,user-agent就是任何能够表现出用户一般行为的工具。但实际上,这个角色通常都是由浏览器来扮演。
简单来说user-agent就是来者何人,留下姓名的意思。
对于发起请求来说,浏览器总是作为发起一个请求的实体,而永远不是服务器(虽然一些机制已经能够模拟服务器发起请求的消息了)。
要渲染出一个网页,浏览器首先要发送第一个请求来获取这个页面的HTML文档,再解析它并根据文档中的资源信息发送其他的请求来获取脚本信息,或者CSS来进行页面布局渲染,还有一些其它的页面资源(如图片和视频等)。然后,它把这些资源结合到一起,展现出来一个完整的文档,也就是网页。打开一个网页后,浏览器还可以根据脚本内容来获取更多的资源来更新网页。
一个网页就是一个超文本文档,也就是说有一部分显示的文本可能是链接,启动它(通常是鼠标的点击)就可以获取一个新的网页。网页使得用户可以控制它的user-agent来导航Web。浏览器来负责翻译HTTP请求的命令,并翻译HTTP的返回消息让用户能明白返回消息的内容。
Web服务端
在上述通信过程的另一端,就是一个Web Server来服务并提供客户端请求的文档。Server只是虚拟意义上:它可以是许多共同分担负载(负载平衡)的一组服务器组成的计算机群,也可以是一种复杂的软件,通过向其他计算机发起请求来获取部分或全部资源的软件。
Server不再只是一个单独的机器,它可以是在同一个机器上装载的许多服务之一。在HTTP/1.1和Host头部中,它们甚至可以共享同一个IP地址。
Proxies
在浏览器和服务器之间,有许多计算机和其他设备转发了HTTP的消息。因为Web栈层次结构的原因,它们大多数都出现在传输层、网络层和物理层上,对于HTTP的应用层来说就是透明的(虽然它们可能会对应用层的性能有重要影响)。而还有一部分表现在应用层上的,就叫做proxies了。Proxies既可以表现得透明,又可以不透明(看请求是否通过它们),主要表现在这几个功能上:
- 缓存(可以是公开的或是私有的,像浏览器的缓存)
- 过滤(像反病毒扫描,家长监护)
- 负载均衡,让多个服务器服务不同的请求
- 对不同资源的权限控制
- 登陆,允许存储历史信息
HTTP 的基本性质
HTTP 是简单的
即便在HTTP/2中把HTTP消息封装到了frames中,HTTP大体上还是被设计成可读的而且简单的。HTTP的消息能够让人读懂且明白它的意思,还允许简单的测试,放低了门槛,更有利于新来者了解。
HTTP 是可扩展的
在HTTP/1中就出现了,HTTP headers让协议扩展变得非常容易。只要服务端和客户端在新的headers上语义达成一致,新的功能就可以轻松地被加进来。
HTTP 是无状态,有会话的
HTTP是无状态的:在同一个连接中,两个成功执行的请求之间是没有关系的。这就带来了一个问题,用户没办法在一个网站进行连续的交互,比如在一个电商网站里,用户把某个商品加入了购物车中,换了一个页面后再次添加商品,两次添加商品的请求没有联系,浏览器无法知道最终用户都选择了哪些商品。而用HTTP的头部扩展,HTTP Cookies就可以解决这个问题。把Cookies添加到头部中,创建一个会话来让每次请求都能共享相同的上下文信息,相同的状态。
而HTTP的核心是无状态的,cookies的使用可以创建有状态的会话。
HTTP 和连接
一个连接是由传输层来控制的,基本不属于HTTP的范围内。然而HTTP并不需要下面传输层的协议是面向连接的,它只需要它是可靠的,就是说不能丢失消息(至少没有错误)。在因特网两个最常用的传输层协议中,TCP是可靠的,而UDP不是。因此,HTTP依赖于TCP进行消息传递,虽然TCP是面向连接的,但这并不是必须的。
HTTP/1.0曾经为每一个请求/回应交换都打开一个TCP连接,导致了2个缺点:打开一个连接需要多次的消息往返因此很慢。但是当多个消息周期性的发送时,这就变得更加高效:暖连接比冷连接更高效。
为了减少这些负担,HTTP/1.1引入了流水线的概念(已被证明很难实现)和持久连接的概念:下层的TCP连接可以通过Connection头部来被部分地控制。HTTP/2则发展的更多,通过一个连接多个消息的方式来让这个连接始终保持为暖连接。
为了更好的适合HTTP,设计一种更好的传输层协议就一直在进行中。Google就研发了一种以UDP为基础,能提供更可靠更有效传输层协议的QUIC
HTTP 能控制什么
多年以来,HTTP良好的扩展性控制着越来越多Web的功能。缓存和认证方式很早就可以由HTTP来控制了。另一方面,对同源同域的限制到2010年才有所改变。
下面就是可以用HTTP来控制的常见特性。
- 缓存
文档怎么缓存能够通过HTTP来控制。服务端能告诉代理和客户端什么需要被缓存,缓存多久,而客户端能够命令中间缓存代理来忽略存储的文档。
- 开放同源限制
为了防止网络窥听和其它的隐私泄漏,浏览器强制对Web网站做了分割限制。只有来自于相同来源的网页才能够获取网站的全部信息。这样的限制有时反而成了负担,HTTP可以通过修改头部来开放这样的限制,因此web文档可以是由不同域下的信息拼接成的(在某些情况下,这样做还有安全因素考虑在里面)。
- 认证
一些页面能够被保护起来,仅让特定的用户进行访问。基本的认证功能(Basic Authenticate)可以直接通过HTTP提供,使用Authenticate相似的头部就可以,或者用HTTP cookies来设定指定的会话。
- 代理
服务端和客户端通常都处在内部网上,彼此的真实地址都是不可见隐藏的。HTTP请求就要通过代理穿过网络障碍。不是所有的代理都是HTTP代理的,像一些用SOCKS协议的代理就运作在更底层(一些其它的协议,像ftp也能够被它们处理)
- 会话
Cookies用一个服务端的状态连接起了每一个请求。这就创建了会话,虽然基本的HTTP是无状态协议。这很有用,不仅是因为能用到购物车这样的电商业务上,更是因为,它使得任何网站都能够配置页面展现的东西了
HTTP 流
当客户端想要和服务端进行信息交互时(服务端是指作为最终的服务器,或者是作为中间代理),过程表现为下面的几步:
打开一个TCP连接(或者重用之前的一个):TCP连接用来发送一条或多条请求,当然也用来接受回应消息。客户端可能重用一个已经存在的连接,或者也可能重开几个新的与服务端的TCP连接。
发送一个HTTP报文:HTTP报文(在HTTP/2之前)是语义可读的。在HTTP/2中,这些简单的消息被封装在了帧中,这使得报文不可能被直接读出来,但是规则仍旧是相同的。
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr
- 读取服务端返回的报文:
HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: "51142bc1-7449-479b075b2891b"
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html
<
!DOCTYPE html... (here comes the 29769 bytes of the requested web page)
- 关闭连接或者为以后的请求重用连接。
HTTP 报文
HTTP/1.1和更早的HTTP报文都是语义可读的。在HTTP/2中,这些报文被嵌入到了一个新的二进制结构中-帧。帧可以允许实现很多优化,如复用和报文头部的压缩。即使只有原始HTTP报文的一部分以这种HTTP/2版本的方式发送出来,每个报文的语义依旧不变,客户端会重组原始的HTTP/1.1请求。因此用HTTP/1.1格式来考虑HTTP/2报文仍旧很有效。
有两种HTTP报文的类型,请求与响应,每种都有其特定的格式。
请求
HTTP请求的一个例子:
请求由下面的元素组成:
- 一个HTTP的method,经常是由一个动词像GET, POST 或者一个名词像OPTIONS,HEAD来定义客户端的动作行为的。通常客户端的操作都是获取资源(用GET方法)或者发送一个HTML form表单的值(用POST方法),虽然在一些情况下也会有其他的操作。
- 要获取的资源的路径,通常是上下文中就很明显的元素资源的URL,它没有protocol (
http://),domain(developer.mozilla.org),或是TCP的port(HTTP是80端口)。 - HTTP协议的版本号。
- 为服务端表达其他信息的可选择性的headers。
- 对于一些像POST这样的方法,报文的body就包含了发送的资源,这个body与回应报文的body类似。
响应
HTTP响应的一个例子:
响应报文包含了下面的元素
- HTTP的版本号。
- 一个状态码(status code),来告知对应的请求发送成功或失败,以及失败的原因。
- 一个状态信息,这个信息是非权威的状态码描述信息,也就是说可以由服务端自行设定的。
- HTTP headers,与请求的很像。
- 可选的,但是比在请求报文中更加常见地包含获取资源的body。
总结
HTTP是很简单可扩展的一种协议。结合了轻松添加头部信息能力的Client-server结构使得HTTP可以和Web的功能扩充一同发展。
即使HTTP/2为了提高性能把HTTP报文嵌到帧中这一举措增加了复杂度,但是从Web应用的角度来看,报文的基本结构是没有变化的,从HTTP/1.0发布起就是相同的。会话流依旧很简单,用一个简单的 HTTP message monitor就可以查看它和debug。