1

序言

这篇文章是应网友之邀所写,紧张描述一下我们访问网站时, 从输入网址到末了浏览器呈现内容,中间发生了什么。

jsp设置文字居中显示图片从输入网址到最后阅读器出现页面内容中央产生了什么 RESTful API

本日的文章紧张专注于运用层,我拿了一个很大略的网络构造来讲。
假定本机已经获取了IP地址,各种网络根本举动步伐已经准备好了。

由于知识点太多,我肯定会漏掉部分内容,欢迎在留言中补充, 往后我会根据大家建议再写文章扩展。

2

准备

当你在浏览器中输入网址(例如www.coder.com)并且敲了回车往后, 浏览器首先要做的事情便是得到coder.com的IP地址,详细的做法便是发送一个UDP的包给DNS做事器,DNS做事器会返回coder.com的IP, 这时候浏览器常日会把IP地址给缓存起来,这样下次访问就会加快。

比如Chrome, 你可以通过chrome://net-internals/#dns来查看。

有了做事器的IP, 浏览器就要可以发起HTTP要求了,但是HTTP Request/Response必须在TCP这个“虚拟的连接”上来发送和吸收。

想要建立“虚拟的”TCP连接,TCP邮差须要知道4个东西:(本机IP, 本机端口,做事器IP, 做事器端口),现在只知道了本机IP,做事器IP, 两个端口怎么办?

本机端口很大略,操作系统可以给浏览器随机分配一个, 做事器端口更大略,用的是一个“众所周知”的端口,HTTP做事便是80, 我们直接见告TCP邮差就行。

经由三次握手往后,客户端和做事器真个TCP连接就建立起来了!
终于可以发送HTTP要求了。

之以是把TCP连接画成虚线,是由于这个连接是虚拟的

3

Web做事器

一个HTTP GET要求经由千山万水,历经多个路由器的转发,终于到达做事器端(HTTP数据包可能被下层进行分片传输,略去不表)。

Web做事器须要动手处理了,它有三种办法来处理:

(1) 可以用一个线程来处理所有要求,同一时候只能处理一个,这种构造易于实现,但是这样会造成严重的性能问题。

(2) 可以为每个要求分配一个进程/线程,但是当连接太多的时候,做事器真个进程/线程会耗费大量内存资源,进程/线程的切换也会让CPU不堪重负。

(3) 复用I/O的办法,很多Web做事器都采取了复用构造,例如通过epoll的办法监视所有的连接,当连接的状态发生变革(如有数据可读), 才用一个进程/线程对那个连接进行处理,处理完往后连续监视,等待下次状态变革。
用这种办法可以用少量的进程/线程应对成千上万的连接要求。

我们利用Nginx这个非常盛行的Web做事器来连续下面的故事。

对付HTTP GET要求,Nginx利用epoll的办法给读取了出来, Nginx接下来要判断,这是个静态的要求还是个动态的要求啊?

如果是静态的要求(HTML文件,JavaScript文件,CSS文件,图片等),大概自己就能搞定了(当然依赖于Nginx配置,可能转发到别的缓存做事器去),读取本机硬盘上的干系文件,直接返回。

如果是动态的要求,须要后端做事器(如Tomcat)处理往后才能返回,那就须要向Tomcat转发,如果后真个Tomcat还不止一个,那就须要按照某种策略选取一个。

例如Ngnix支持这么几种:

轮询:按照次序挨个向后端做事器转发权重:给每个后端做事器指定一个权重,相称于向后端做事器转发的几率。
ip_hash: 根据ip做一个hash操作,然后找个做事器转发,这样的话同一个客户端ip总是会转发到同一个后端做事器。
fair:根据后端做事器的相应韶光来分配要求,相应韶光段的优先分配。

不管用哪种算法,某个后端做事器终极当选中,然后Nginx须要把HTTP Request转发给后真个Tomcat,并且把Tomcat输出的HttpResponse再转发给浏览器。

由此可见,Nginx在这种场景下,是一个代理人的角色。

4

运用做事器

Http Request终于来到了Tomcat,这是一个由Java写的、可以处理Servlet/JSP的容器,我们的代码就运行在这个容器之中。

犹如Web做事器一样, Tomcat也可能为每个要求分配一个线程去处理,即常日所说的BIO模式(Blocking I/O 模式)。

也可能利用I/O多路复用技能,仅仅利用多少线程来处理所有要求,即NIO模式。

不管用哪种办法,Http Request 都会被交给某个Servlet处理,这个Servlet又会把Http Request做转换,变成框架所利用的参数格式,然后分发给某个Controller(如果你是在用Spring)或者Action(如果你是在Struts)。

剩下的故事就比较大略了(不,对码农来说,实在是最繁芜的部分),便是实行码农常常写的增编削查逻辑,在这个过程中很有可能和缓存、数据库等后端组件打交道,终极返回HTTP Response,由于细节依赖业务逻辑,略去不表。

根据我们的例子,这个HTTP Response该当是一个HTML页面。

5

归途

Tomcat很高兴地把Http Response发给了Ngnix 。

Ngnix也很高兴地把Http Response 发给了浏览器。

发完往后TCP连接能关闭吗?

如果利用的是HTTP1.1, 这个连接默认是keep-alive,也便是说不能关闭;

如果是HTTP1.0,要看看之前的HTTP Request Header中有没有Connetion:keep-alive,如果有,那也不能关闭。

6

浏览器再次事情

浏览器收到了Http Response,从个中读取了HTML页面,开始准备显示这个页面。

但是这个HTML页面中可能引用了大量其他资源,例如js文件,CSS文件,图片等,这些资源也位于做事器端,并且可能位于其余一个域名下面,例如static.coder.com。

浏览器没有办法,只好一个个地下载,从利用DNS获取IP开始,之前做过的事情还要再来一遍。
不同之处在于不会再有运用做事器如Tomcat的参与了。

如果须要下载的外部资源太多,浏览器会创建多个TCP连接,并行地去下载。

但是同一韶光对同一域名下的要求数量也不能太多,要不然做事器访问量太大,受不了。
以是浏览器要限定一下, 例如Chrome在Http1.1下只能并行地下载6个资源。

当做事器给浏览器发送JS,CSS这些文件时,会见告浏览器这些文件什么时候过期(利用Cache-Control或者Expire),浏览器可以把文件缓存到本地,当第二次要求同样的文件时,如果不过期,直接从本地取就可以了。

如果过期了,浏览器就可以讯问做事器端,文件有没有修正过?(依据是上一次做事器发送的Last-Modified和ETag),如果没有修正过(304 Not Modified),还可以利用缓存。
否则的话做事器就会被最新的文件发回到浏览器。

当然如果你按了Ctrl+F5,会逼迫地发出GET要求,完备忽略缓存。

注:在Chrome下,可以通过 chrome://view-http-cache/ 命令来查看缓存。

现在浏览器得到了三个主要的东西:

1.HTML ,浏览器把它变成DOM Tree

2. CSS, 浏览器把它变成CSS Rule Tree

3. JavaScript, 它可以修正DOM Tree

浏览器会通过DOM Tree和CSS Rule Tree天生所谓“Render Tree”,打算每个元素的位置/大小,进行布局,然后调用操作系统的API进行绘制,这是一个非常繁芜的过程,略去不表。

到目前为止,我们终于在浏览器中看到了www.coder.com的内容。

本文转自"大众号:码农翻身