1
序言
这篇文章是应网友之邀所写,紧张描述一下我们访问网站时, 从输入网址到末了浏览器呈现内容,中间发生了什么。
本日的文章紧张专注于运用层,我拿了一个很大略的网络构造来讲。假定本机已经获取了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的内容。
本文转自"大众号:码农翻身