从百度的搜索结果来看,能够搜到七千多万条记录,因此本篇不会再以那种前篇一律的办法赘述,而因此目前较新的网络内容,结合系统中的大部分做事,将自己类比成一个要求,切身感想熏染到每个技能栈的详细细节,彻底从“根儿上”理解客户端要求-做事端相应的全过程。
一、地址栏输入后本地会发生的事情当我们在浏览器的地址栏中,输入xxx内容后,浏览器的进程首先会判断输入的内容:
如果是普通的字符,那浏览器会利用默认的搜索引擎去对付输入的xxx天生URL。如若输入的是网址,那浏览器会拼接协议名形成完全的URL。当然,在地址栏中输入某个内容后,也会进行一些额外操作,例如:安全检讨、访问限定等,但总归而言,浏览器做的第一件事情则是天生URL,当按下回车后,浏览器进程会将天生的完全URL发送到网络进程:
当网络进程收到传过来的URL后,首先并不会直接发出网络要求,而是会先查询本地缓存:
不雅观察上述流程,当网络进程收到传来的URL后,会首先通过URL作为Key,在本地缓存中进行查询:
①如果本地中是否有缓存: 没有:发起网络要求去做事器获取资源,成功后将结果渲染页面并写入缓存。 有:连续判断本地中的缓存内容是否已经由期,没有则直策应用本地缓存。②如果本地中的缓存已经由期,则会携带If-Modified-Since、If-None-Match等标识向做事器发起要求,先判断做事器中的资源是否更新过: 未更新:做事器返回304状态码,并连续读取之前的缓存内容利用。③如若做事器的资源更新过,那么也会向做事器发起要求获取资源。如果在本地缓存中,无法命中缓存,或者本地缓存已过期并做事器资源更新过,那么此刻网络进程才会真正向目标网站发起网络要求。
二、一个全新的“我”出身过程与前期的经历当客户真个网络进程,在查询缓存无果后,会真正开始发送网络要求,但要牢记:客户真个网络进程并非直接向目标网站发起要求的,前期还需经由一些细节处理。
当然,为了能够更直不雅观地感想熏染全体过程,在这里我们将自己“化身”为一个要求,站在要求的角度切身体验一段奇特的“网络旅途”。
2.1、“我”出身前的准备 - 解析URL在网络进程发起要求之前,会首先对浏览器进程传过来的URL进行解析,一样平常来说完全的URL构造如下:
但上述构造利用较少,常日情形下,浏览器会利用的URL的常用构造如下:
URL中每个字段的释义如下:
scheme:表示利用的协议类型,例如http、https、ftp、chrome等。://:协议类型与后续描述符之间的分隔符。domainName:网站域名,经DNS解析后会得到详细做事器IP。/path:要求路径,代表客户端要求的资源所在位置,不同层级目录之间用/区分。?query1=value:要求参数,?后面表示要求的参数,采取K-V键值对形式。&query2=value:多个要求参数,不同的参数之间用&分割。#fragment:表示所定位资源的一个锚点,浏览器可根据这个锚点跳转对应的资源位置。网络进程会根据URL的构造对目标URL进行解析,个中有两个关键信息:
首先会解析得到协议名,例如http、https,这关乎到后续默认利用的端口号。然后会解析得到域名,这个将关乎到后续详细要求的做事器地址。假设浏览器传输过来的URL为https://juejin.cn/user/862486453028888/posts,那么在这个阶段会确定后续要求的做事器端口号为443,要求的目标域名为www.juejin.cn。其实在这里紧张是根据浏览器的输入信息,去解析出一些“出身我(要求)”的前置要素。
2.2、“我”该去往的详细位置 - DNS域名解析 在上个阶段已经大概知道“我”该去往何处啦!
但我详细地址该到那里呢?“我”彷佛不大清楚,要不找个人问问吧^_^。我记得彷佛有个叫做DNS的“大家族”是专门卖力这个的!
我要去找它们问问看~
不过在问DNS之前,我先来看看本地有没有域名与IP的映射缓存,彷佛没有~,那我只能去找DNS了(-_-),我首先找到了「本地DNS大叔」,把我要查找的域名交给了它,它让我稍等少焉,它给我找一下,让我们一起来看看「本地DNS大叔」是怎么查找的:
①首先「本地DNS大叔」找了它的「根DNS族长」,族长见告它该当去找「顶级DNS长老」。②「本地DNS大叔」根据族长的示意去找了「顶级DNS长老」,然而长老又见告它该当去找「授权DNS执事」。③「本地DNS大叔」又根据长老的示意找到了「授权DNS执事」,终极在「授权DNS执事」那里查到了我手里域名对应着的详细IP地址。④「本地DNS大叔」拿着从「授权DNS执事」那里查到的IP,终极把它交给了我,为了下次不麻烦大叔,以是我获取了IP后,将其缓存在了本地。呼~,我终于知道我该去哪儿啦!
准备出发咯!
问过DNS大叔后,得到了目的地址的我,此时已经知道该去往何处啦!
但在正式出发前,由于前路坎坷,途中会存在各种危急(网络壅塞、网络延迟、第三方挟制等),因此为了我的安全出行,首先还需为我建立一条安全的通道,以是我还须要等一下子才能出发,俺们一起来瞅瞅建立安全通道的过程是什么样的:
看着好繁芜啊~,但彷佛大体就分为了两个过程:
首先是TCP的三次握手过程,听说这个阶段是为了确保目的地能够正常吸收我、也是为了给我建立出一条可靠的出行通道、并且为我打算一下出行失落败之后多久重新出发的韶光等目的(也便是为了测试双方是否能正常通信、建立可靠连接以及预测超时时间等)。
实在按照之前的“交通规则”,在建立好TCP连接之后,我就可以连续走下一步啦,但现在有很多坏人,在我们出行的道路上挟制我们,然后盗取、修改俺们携带的数据,以是如今出行变得很不屈安,因此还须要还须要建立一条安全的出行通道,便是TLS大叔的安全连接~(HTTP+TLS=HTTPS):
TLS握手阶段,在这个阶段中,TLS大叔为了俺的安全出行,会通过很多手段:非对称加密、对称加密、第三方授权等,先和俺的目的地交流一个密钥,然后再通过这个密钥对我加密一下,确保我被坏人抓到了也无法得到俺护送的数据^_^!
详细且专业性的过程请参考之前的:《计网根本TCP/IP综述-TCP三次握手》、《全解HTTP/HTTPS-SLL、TLS详解》。
2.4、出身“我的身体” - 构建要求报文 经历上述过程后,安全的出行道路已经建立好啦!
但此刻的我还不算完全,以是须要先构建一个“身体”,也便是HTTP要求报文:
“我的身体”紧张由要求行、要求头、空行以及要求主体四部分组成,里面包含了“我本次出远门的须要护送的数据和一些其他信息”。同时,为了我能够在“出行的道路上(传输介质)”安全且正常传输,我还须要经由层层封装:
首先为了确保俺护送的数据安全,TLS大叔会先对我的数据进行一次加密,把我原来携带的明文数据转变为看都看不懂的密文,类似下面这个样子:
经由加密后的我会紧接着来到传输层,传输层会在我的脑袋上再贴上一个传输头,如果是TCP大哥的话,它会给我贴上一个TCP头,但如果传输层的UDP大哥在的话,它给我贴的便是UDP头。但不管是谁贴的,在这个传输头内,为了防止我迷路和走丢,TCP、UDP两位大哥哥都会细心的在里面写清楚“我来自哪里,该去往何处”,也便是源地址和目的地址:
偷偷吐槽一句:TCP大哥贴的传输头里面,放了好多好多东西,让我觉得脑袋沉沉的。
过了传输层这一站之后,我又来到了网络层,果不其然,网络层里面最常见的还是IP大叔,IP大叔看到我之后,又在我的脑袋上贴上了一个网络头,也便是给我又加了一个IP头。
哒哒哒~,我出了网络层这关之后,又来到了数据链路层,这关则是由大名鼎鼎的“以太网家族”驻守,在这里我和之前两关不同,除开在我脑袋上贴了一个链路头之外,还给我在尾巴上多加了一个链路尾。
不过刚刚出链路层的时候,彷佛有个人跟我说:你这个样子是无法在介质上行走的,你要记得改变一下啊!
我还没听得太清楚,就来到了物理层这关,这层和之前我“家里”以及之前的关卡环境都不一样,物理层的小伙伴们彷佛都有实际的形态,但之前打仗所有内容都是虚拟的观点形态哎~。
在我比拟物理层大哥们的异样差距时,一不愣神创造我的身体彷佛发生了“翻天覆地”的变革,全体我彷佛都变为了0、1构成了,正当纳闷时,物理层的某个大哥哥见告我说:“只有变成这样子,你才可以在出行的道路上行走哈,以是我们给你转换了一下形态,你现在已经可以出发了”。
原来是这样呀,彷佛链路层的时候有人跟我说过哎~
2.5、踏上路途的我 - 数据传输GO~GO~GO~,终于出发啦!
我终于踏上了网络之旅!
呼呼呼~
咔!
我来到了第一个中转站,听别人说,彷佛它的名字叫做路由器,首先路由器大哥把我的身体按照之前封装的步骤层层解封了,但解封到传输层的时候,看到了我脑袋上的传输头,彷佛路由器大哥创造了TCP哥哥写的目的地址,创造我的目的地还在更远的位置,然后路由器大哥又按照原来的步骤把我的身体封装回去了,然后还亲切的给我指出了接下来该往那条路走,我又该连续前行啦....
啊!
路途好迢遥呀,我一起走了良久良久,也碰着了很多很多的中转站,每次当我不知道怎么走时,路由器大哥都会温馨的给我指出接下来该走的路途。期间我也走过很多很多路,曾踩着双绞铜线、同轴电缆、光纤前行,当然,可不要鄙视俺,就算没有物理连接的情形下,我也可以通过无线电技能,通过空气前行呢!
走着走着,溘然前方碰着一个叫做CDN的老爷爷,它问我说要去哪里,我说要去xx地方办事,和蔼的CDN老爷爷跟我说,我来看看我这里有没有你要的东西,如果有的话,就不用麻烦你这个小家伙一贯跑下去了。可是很遗憾,CDN老爷爷说它哪儿没有我要的东西,因此我只能连续前行下去。
记不清过了多久,一起跌跌撞撞,在迷迷糊糊中我来到了一个地方,但当我还在分辨时,刷得一下,很快啊,我就被丢到了其他地方,当我转头看的时候,创造刚刚哪个地方,大写着LVS。
再直视前方,前方有一个东西很眼熟,难道这便是当初听说过的做事器吗?带着一脸迷惑的我逐步走了进去,我创造内部空间很大,上面漂浮着一块大陆,名为Linux大陆,上面有好多好多的“城市(进程)”林立着,那我该去哪一座呢?让我想想!
让我回忆一下,HTTP的默认端口是80,HTTPS的默认端口是443,我目前属于HTTPS派别的要求,那么我该当去找编号为443的城市!
出发出发~
顺着我的推理,我来到了编号443城市的城门口,当我迈进城门后,嗖的一下,我被一个叫做Nginx的大叔抓了过去....
Nginx:小家伙,你是来干嘛的?我:我带了一些数据过来找地址为IP:443的地方办事!Nginx:噢~,原来是这样啊,我便是卖力监听443编号的守门将。Nginx:小家伙,你过来让我看看....
话音刚落,Nginx三下五除二的就把我的身体拆开了,然后得到了HTTP报文,然后从HTTP报文的要求行中,创造了我本次旅途的详细目标:/user/862486453028888/posts,然后Nginx大叔又把我组装了回去,然后根据它内部配置的规则,然后道:
Nginx:小家伙,我刚才看了一下,你该当要去的详细位置是xxx.xxx.xxx.xxx:xx,快去吧。我:你怎么知道我要去的是这里?Nginx:我刚刚看了一下,你要去的详细位置为IP:443/user/....,根据目前的规则以及我代理的地址,你就该当去这里!我:大叔大叔,给我看看你代理了那些地址呗。Nginx:你可以过来看看。我:哇,为什么这么多!
我可不可以去找其他的地址,找其他人帮我办事呀?Nginx:不可以噢!
按照规则的话,你就该当去我给你的地址哈。我:好吧,那我去啦!
顺着Nginx大叔给的地址,我又来到了其余一台做事器,上面同样有一块Linux大陆,然后根据地址在上面找到了一个名为Gateway的东东,听它自己先容,彷佛属于系统网关。但当我找它办事时,它却跟我说:“我不卖力详细的业务处理,根据你的目标/user/....,你该当去找Nacos注册局,问它们要一下USER-SERVICE的详细地址,以是,小家伙你还得连续奔波哦”!
哒哒哒~,迈着愉快的步伐我来到了Nacos注册局,然后将Gateway叔叔给我的名字:USER-SERVICE交给了它们的事情职员,它们的事情职员经由一番查询之后见告我,这个“品牌”多有个分部,你可以去个中任意一处罚部处理你的任务,你可以去:xxx.xxx.xxx.xxx:8080这个地址噢!
好的好的,那我就去你说的这个xxx.xxx.xxx.xxx:8080地址啦!
我一边在路上走着,一边想了一下刚刚过程发生的事情,然后把这个经历画成了一副逻辑图,如下:
回去的时候我一定要跟小伙伴们分享一下这个有趣的经历,耶!
根据Nacos给我的地址,我又来到了一台新的做事器面前,我记得Nacos给了我一个端口号,要我来到这里之后找编号为8080的位置,我顺着这个编号逐步找着,溘然在我的前方,涌现了一只大老虎,哦不,该当是一只大猫咪,它长这个样子:
它的长相似乎有些报看,但在它的脑门上恰好写着我要找到8080地址,那我要找的该当便是它了吧!
终于到了!
我逐步靠近了这只大猫咪,然后跟它说要找它办事,Tomcat说要看看我的数据,然后又把我的身体按照之前封装的办法逆向拆开了,从而还原了我最初的身体-HTTP要求报文,末了Tomcat说:“我确实是你本次要找的终极目标,不过要办你这件事情得到我肚子里面去噢”!
说罢,Tomcat伸开了它的血盆大口,一口将我吞了下去.....,正当我以为我塌台的时候,我却创造Tomcat内部别有乾坤,上面彷佛也有一块小陆地漂浮着,当我凑近的时候才看清楚,原来上面写的是JVM呀!
我二话不说,一脚踏上了这块陆地,正当我看着上面密密麻麻的“屋子(Java方法)”迷茫时,此时我正前方就走来了一个人,然后对我做了一个自我介绍:
来自远方的尊敬客人,您好呀,欢迎光临JVM神州,我叫Thread-xxx,是线程家族的一员,您接下来的全体旅途,我终将陪伴在您旁边,您须要办的所有事情,都会由我代劳,客官这边请(45度鞠身)~
然后我一边走着,一边跟Thread-xxx聊着:
我:为什么是你来接我呀?线程:由于每位从远方到来的客人,我们线程家族都会叮嘱消磨一位子弟欢迎。线程:本次轮到我了,因而由我为您本次的旅途供应做事。我:噢噢噢,那我们接下来该去哪儿呢?线程:这须要看客官您本次的目的啦!可以让我看看您本次的旅程吗?我:可以呀,看吧,[我将要求要求行中的资源地址摆了出来]。线程:/user/....,原来您是要去这里呀,这边请~。线程:我们首先要去找DispatcherServlet办事处,才能连续前行。
PS:接下来是讲述Java-SpringMVC框架的实行过程,非Java开拓可忽略细节。
随着Thread-xxx的步伐,我们找到了线程口中所说的DispatcherServlet办事处,该办事处的事情职员首先看了一下我本次的详细目的地(资源地址),然后说:您须要先去问一下HandlerMapping管理局,让它给你找一下详细卖力这块业务的事情室。
线程Thread-xxx道:这便是卖力您本次任务的终极事情室啦!
我这就带您过去。
先上一张SpringMVC的事理图:
不雅观察如上流程图,实在看起来难免有些生涩,那此刻咱们换成大略一点的办法阐述,不再通过这种源码性的流程去理解。
不知诸位是否还记得,最开始学习SpringMVC时的配置过程,接下来我们大略回顾一下:
①配置springmvc-servlet.xml文件:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 通过context:component-scan元素扫描指定包下的掌握器--> <!-- 扫描com.xxx.xxx及子子孙孙包下的掌握器(扫描范围过大,耗时)--> <context:component-scan base-package="com.xxx.xxx"/> <!-- ViewResolver --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- viewClass须要在pom中引入两个包:standard.jar and jstl.jar --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean></beans>复制代码
在这第一步中,最主要的便是配置一下扫描包的位置,以及配置一下视图解析器。
②配置web.xml:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <display-name>Archetype Created Web Application</display-name> <!-- Spring MVC servlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <!--配置一下DispatcherServlet的位置--> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--指定springMVC的初始化文件位置,默认值为:/WEB-INF/springmvc-servlet.xml--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <!--web.xml 3.0的新特性,是否支持异步--> <!--<async-supported>true</async-supported>--> </servlet> <!--关键!
!
!
配置一条要求路径映射,"/"代表匹配所有路径的要求--> <!--也便是当有要求到来时,都会被进入前面servlet-name=SpringMVC的servlet中--> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>复制代码
在第二步中,紧张会配置一条要求路径的映射位置,将进入WEB程序的所有要求全部转入DispatcherServlet的doGet、doPost方法中。同时由于web.xml中配置了一个servlet:DispatcherServlet,以是在程序启动时,首先会加载DispatcherServlet,加载时会实行初始化操作,会调用initStrategies()方法:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context);}复制代码
重点看个中的第四个初始化操作:调用initHandlerMappings()方法,由于之前在web.xml中指定了初始化文件的位置:/WEB-INF/springmvc-servlet.xml,那么紧接着SpringMVC会去读取该配置文件中的base-package扫描包路径,然后会开始扫描这个包路径下的所有类:
首先会扫描出该包中带有@Controller表明的类。然后会对扫描出的所有类进一步做扫描,会扫描到所有方法上存在@RequestMapping表明的方法。末了会以两个表明上配置的值组合起来做为Key,然后通过反射机制,将方法变为一个Method工具,封装成InvocationHandler实例作为Value,一起加入到一个大的Map容器中。上述流程下来大致诸位有些晕乎乎的,那么大略举个例子:
@Controller("/user")public class UserController { @RequestMapping("/get") public String get(User user) { ...... } @RequestMapping("/add") public String add(User user) { ...... }}复制代码
上述这个案例中,终极在初始化之后,会被以下面这种形式加入Map容器:
// 这里是伪代码,紧张是为了阐述逻辑Map<String,InvocationHandler> map = new HashMap<>();// 后面的UserController#get()便是以反射获取到的Method方法实例map.put("/user/get",InvocationHandler(UserController#get()));map.put("/user/add",InvocationHandler(UserController#add()));复制代码
终极当要求到来时,由于之前web.xml中配置了一条/匹配规则,所有的要求都会被转入到DispatcherServlet的doGet、doPost中,在该方法内首先会以HTTP要求报文-要求行中的资源路径作为Key,然后在这个Map容器里面进行匹配,从而定位到详细的Java方法并实行。
OK,末了在大略的把完全流程阐述一遍:
其实在咱们把一个JavaWeb程序打成war包丢入Tomcat并启动时,Tomcat就会先去加载web.xml文件。在加载web.xml配置文件时,会碰到DispacherServlet须要被加载。当加载DispacherServlet时,实在便是把SpringMVC的组件初始化,以及将所有Controller中的URL资源全部映射到容器中存储。然后当要求进入Tomcat经由DispacherServlet时,DispacherServlet就去容器中找到这个要求的URL资源。找到要求的资源路径对应的Java方法后,会调用组件通过反射机制去实行详细的Controller方法。当实行完毕之后,又会回到DispacherServlet,此时DispacherServlet又会去调用干系组件处理实行后的结果。末了当结果处理完成后,才会将渲染后的结果相应回客户端。线程:客官,咱们到了!线程:这个事情室中已经写明了您本次任务如何处理的详细步骤,接下的事情都将由我为您效劳。线程:您要随我一起去看看详细的处理过程嘛?我:好呀,好呀,一起去!
随着线程的事情开始,我们一起走过了service层、dao/mapper层,在service层办事时,我们碰着了强大的Redis哥哥,Redis哥哥看到我们之后,问清楚了我们本次到来的目的,然后它说:“来自远方的贵客,请稍等,让我先看看我这里有没有您须要的东西!
”
这个场景似曾相识哎,我记得来的路上也有个CDN老爷爷跟我说过同样的话~
Redis:来自远方的客人,很抱歉我这里没有您要的东西。Redis:您本次的路途还需连续前行,您可以去找一下MyBatis哪小子,它大概能够帮到您。根据Redis的指示,线程Thread-xxx领着我终极见到了MyBatis,它长这个样子:
原来Redis哥哥口中的MyBatis竟然是个鸟叔叔[吐舌~]
MyBatis大略看了一下我本次的任务:
鸟叔:来自远方的贵客,这件事我确实可以帮到您,请稍等。然后“鸟叔”一顿操作,竟鼓捣出了一个我看不懂的东西,然后递给了我。鸟叔:这个叫做SQL代码,是你您次任务的必须之物。鸟叔:你现在可以拿着它,让Thread-xxx去带您找一下JDBC哪个老家伙。逐步的,线程又带我找到了“鸟叔”口中所说的JDBC老爷爷,JDBC老爷爷见到我的到来,眼神中并没有丝毫的意外之情,彷佛早已经习以为然,只见JDBC老爷爷抬起瘦削的右手,指着一个地址:
然后道:“小家伙,你又须要再跑一段远路咯,而且只能你去,Thread-xxx只能在这里等你”。
我:好吧好吧,那我去啦!
又是孤身一人的旅途,难免有些孤独感袭来,但还好我早已习气啦!
随着一起奔波,我来到了JDBC老爷爷给出的地址,这里同样是位于其余一台做事器的Linux大陆上,我通过3306这个编号找到了一座叫做MySQL的城池,当我踏入之后创造,与之前踏上JVM神州相同,在我刚踏入MySQL这座大城的时候,有一个自称为DB连接家族的弟子接待了我。
正当我吃完一块西瓜、喝完一瓶冰阔乐时,DB连接家族的哪位弟子便回来了,同时怀里抱着一大堆东西(数据),然后丢给了我,道:“这便是您本次须要的数据啦,您本次的任务我都按照清单(SQL)上的记录,给您逐一处理了噢”。
我:好的,万分感谢,那我走啦!
顺着来时的原路,我飞速的赶回了JVM神州所在的位置,然后映入眼帘的第一眼便是:Thread-xxx哪个家伙在原地站着,老诚笃实的期待着我的回归,我悄悄的绕到了Thread-xxx身后,然后从背后拍了一巴掌:
我:嘿,我回来啦!等了我这么久,有没有想我~线程:并未,我是在履行线程家族该有的职责。我:额....,无趣。我:我事情已包办妥了,我要走了噢。线程:好的,那由我来送您。
一起跟随着Thread-xxx的脚步,兜兜转转的我们终极又回到了DispatcherServlet办事处,经由它们内部职员的一顿操作之后,我就打算返航啦!
一起走走停停,我走到了JVM神州的边缘。
再见啦!
我:拜拜[挥手]~
我告别了Thread-xxx,也从此离开了JVM神州,终极我从Tomcat这只大猫咪的口中飞了出来,正式踏上了归途。
四、大功告成的我该返航咯 - 做事器相应诸多经历过后,现在的我携带着本次任务的结果踏上了回家之路,首先我又途经了Gateway叔叔那里,然后我又回到了Nginx大叔所在的城池,不过Nginx大叔把我的身体改为了应答报文构造,并且往个中还写入了一些东西,听说是让我回去交给浏览器老大的。
然而在我返航之前,彷佛这边也有加密层、传输层、网络层、链路层、物理层这些关卡,和我当时出发的过程一样,我身上被一层一层的贴了很多东西,并且终极也被改为了0、1组成的身体构造,这个过程是多么的熟习呐!
我又踏上了哪不知有多迢遥的路途,与来时的路一样,个中也碰着了很多中转站,也走过各种各样的道路,当然,为了防止我迷路,在Nginx大叔那里,也在我的脑袋上贴了一个TCP头,里面写清楚了我来自那里,该去向何方.....
在迷迷糊糊中不断前行,终于看到了我的出生地,看到了网络进程和浏览器老大~,哦豁!
我回来啦!
在进入家门之前,我又会经历物理层、链路层、网络层、传输层、TLS层依次解封的过程,紧张是为了将我从后端带回来的数据解析出来。网络进程在解析到数据后,我的义务就此完成啦!
紧接着网络进程会将数据交给浏览器老大,然后老大会叮嘱消磨一个小弟(渲染进程)对数据进行处理,我瞅了几眼,大体过程是这样的:
首先渲染小弟会根据HTML、CSS数据天生DOM构造树和CSS规则树。然后结合构造树和规则树天生渲染树,再根据渲染树打算每一个节点的布局。末了根据打算好的布局绘制页面,绘制完成后关照另一个小弟(呈现器)显示内容。
末了,由于我至此已经正常返航了,所以为了节省资源开销,会将我出发前构建的安全通道(TCP、TLS连接)关闭,这个过程会由TCP大哥去经由四次挥手完成,如下:
五、网络之旅篇总结
综上所述,用户在浏览器地址栏输入内容后,我们站在一个“网络要求”的角度,切身感想熏染了一场奇妙的网络之旅,从客户端发送要求到做事端返回相应,全体流程咱们都“亲自”体验了一回,末了写个流程总结:
①用户在地址栏输入内容,浏览器判断后天生相应的URL并传给网络进程。②网络进程先查询本地缓存,没有则解析URL并向DNS发送要求,得到IP。③网络进程先与目标做事器进行TCP、TLS多次握手,建立TCP、TLS安全连接。④紧接着组装要求报文,并由各个分层对数据进行封装,终极转为0、1格式。⑤基于建立好的连接,利用物理介质传输数据,通过路由器掌握数据的传输方向。⑥要求会先去到CDN查询是否有缓存的内容,如果没有则连续向下要求。⑦要求来到LVS后被转发到Nginx,再由Nginx转发到Gateway网关。⑧Gateway网关根据配置好的API分发规则,将要求分发到详细做事。⑨紧接着再从Nacos注册中央内,查询出该做事的详细做事实例IP。⑩要求来到详细的做事器后,先通过端口号找到详细的WEB做事进程Tomcat。⑪Tomcat基于SpringMVC的事情流程为要求定位到详细的Java后端方法。⑫线程实行Java方法时,先去Redis中查询是否有数据,没有则查询MySQL。⑬查询DB前先通过MyBatis天生SQL语句,然后再通过DB连接实行SQL。⑭要求根据已配置的数据源地址,来到MySQL并实行SQL语句,从而得到数据。⑮经由报文组装、数据封装、要求转发等操作,向客户端相应数据(原路返回)。⑯应答报文经物理介质传输后,终极抵达客户端网络进程(可能会将数据加入缓存)。⑰网络进程将数据交给浏览器之后,根据情形准备做TCP四次挥手,断开连接。⑱浏览器创建渲染子进程,然后根据数据天生渲染树,末了绘制并显示页面。至此全体流程结束,当然,这个过程中并未涉及到太多的技能栈,也包括对付全体前/后端系统内部的实行细节并未阐述,这是由于全体系统的全细节实行流程较为弘大,展开阐述之后难以扫尾,因而在本篇中则捉住核心点去叙说。