阿里面试:“说一下从 url 输入到返回请求的过程”
阅读本文大概需要 8.5 分钟。
前言
问题: 从浏览器地址栏输入url到请求返回发生了什么?
先说为什么url要解析(也就是编码)
我回答大概内容是:因为网络标准规定了URL只能是字母和数字,还有一些其它特殊符号(-_.~ ! * ' ( ) ; : @ & = + $ , / ? # [ ],特殊符号是我下来查的资料,实在背不住这么多,比较常见的就是不包括百分号和双引号),而且如果不转义会出现歧义,比如 http:www.baidu.com?key=value
,假如我的key
本身就包括等于=
符号,比如ke=y=value
,就会出现歧义,你不知道=
到底是连接key
和value
的符号,还是说本身key
里面就有=
。大佬接着毒打我说,那url编码的规则是什么呢,我说utf-8 大佬接着穷追不舍,为啥是utf-8呢,所有浏览器都是这样吗?中文的话用gb2312编码吗,还有就是万一浏览器不是你说的这样统一用utf-8,你怎么保证都是utf-8的编码? 我支支吾吾的说,我了解的大概是这样,不太清楚, 应该和html本身的编码格式有关,然后怎么保证utf-8的编码,我觉得可以用encodeURIComponent 大佬说encodeURIComponent比encodeURI有什么区别? 区别就是encodeURIComponent编码范围更广,适合给参数编码,encodeURI适合给URL本身(locaion.origin)编码,当然项目里一般都是用qs库去处理
然后说说dns解析流程,并且html如何做dns优化
<meta http-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
第一次握手:主机A发送位码为SYN=1的TCP包给服务器,并且随机产生一个作为确认号(这是tcp包的一部分),主机B收到SYN码后直到A要求建立连接; 第二次握手:主机B收到请求后,向A发送确认号(主机A的seq+1),syn=1,seq = 随机数 的TCP包; 主机A收到后检查确认号是否正确,即第一次A发送的确认号是否+1了,以及位码ack是否为1,若正确,主机A会再发送确认号(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
接着,大佬说出个加分题,我看你不是科班出身,能答多少是多少。问题是,从网卡把数据包传输出去到服务器发生了什么,提示我OSI参考模型
我说,先从局域网把数据发送到公司的交换机(如果交换机没有缓存本地mac地址和IP地址的映射,此时会通过ARP协议来获得),交换机的好处是可以隔离冲突域(因为以太网用的是CSMA/CD协议,这个协议规定网线上同一时刻只能有一台机器发送数据),这样就可以不仅仅同一时刻只有一台机器发送网络包了 然后交换机再将数据发送到路由器,路由器相当于公司网关(我们公司小),路由器具有转发和分组数据包的功能(路由器通过选定的路由协议会构造出路由表,同时不定期的跟相邻路由器交换路由信息),然后这算是经过了物理层,数据链路层(以太网),开始到网络层进行数据转发了 然后路由器转发IP数据报,一般公司的IP地址都会经过NAT转换,让内网的ip也能够访问外网,我们公司我注意了一下是192.168打头的内网ip地址。通过路由器的分组传输,所有数据到达服务器。 然后服务器的上层协议传输层协议开始发挥作用,根据tcp包里的端口号,让服务器特定的服务来处理到来的数据包,并且tcp是面向字节流的(tcp有四大特性,可靠传输、流量控制、拥塞控制、连接管理),所以我们node的request对象,它的监听事件data事件为什么要用字符串一起拼接起来呢(buffer),就是因为tcp本身就是字节流,request对象使用的data(http层面)是tcp传来的数据块。 最后数据由传输层转交给应用层,也就是http服务(或者https),后端经过一系列逻辑处理,返回给前端数据。
答完这里,我说大佬我只知道大概的流程,具体细节我不是很清楚,但自己后面会补上。。。
浏览器首次加载资源成功时,服务器返回200,此时浏览器不仅将资源下载下来,而且把response的header(里面的date属性非常重要,用来计算第二次相同资源时当前时间和date的时间差)一并缓存; 下一次加载资源时,首先要经过强缓存的处理,cache-control的优先级最高,比如cache-control:no-cache,就直接进入到协商缓存的步骤了,如果cache-control:max-age=xxx,就会先比较当前时间和上一次返回200时的时间差,如果没有超过max-age,命中强缓存,不发请求直接从本地缓存读取该文件(这里需要注意,如果没有cache-control,会取expires的值,来对比是否过期),过期的话会进入下一个阶段,协商缓存 协商缓存阶段,则向服务器发送header带有If-None-Match和If-Modified-Since的请求,服务器会比较Etag,如果相同,命中协商缓存,返回304;如果不一致则有改动,直接返回新的资源文件带上新的Etag值并返回200; 协商缓存第二个重要的字段是,If-Modified-Since,如果客户端发送的If-Modified-Since的值跟服务器端获取的文件最近改动的时间,一致则命中协商缓存,返回304;不一致则返回新的last-modified和文件并返回200;
我说强缓存会触发,这两种,具体什么行为不知道,大概内容如下:
1、先查找内存,如果内存中存在,从内存中加载;
2、如果内存中未查找到,选择硬盘获取,如果硬盘中有,从硬盘中加载;
3、如果硬盘中未查找到,那就进行网络请求;
4、加载到的资源缓存到硬盘和内存;
// Date 减去 Last-Modified 值的 10% 作为缓存时间。
// Date:创建报文的日期时间, Last-Modified 服务器声明文档最后被修改时间
response_is_fresh = max(0,(Date - Last-Modified)) % 10
构建DOM树(DOM tree):从上到下解析HTML文档生成DOM节点树(DOM tree),也叫内容树(content tree); 构建CSSOM(CSS Object Model)树:加载解析样式生成CSSOM树; 执行JavaScript:加载并执行JavaScript代码(包括内联代码或外联JavaScript文件); 构建渲染树(render tree):根据DOM树和CSSOM树,生成渲染树(render tree); 渲染树:按顺序展示在屏幕上的一系列矩形,这些矩形带有字体,颜色和尺寸等视觉属性。 布局(layout):根据渲染树将节点树的每一个节点布局在屏幕上的正确位置; 绘制(painting):遍历渲染树绘制所有节点,为每一个节点适用对应的样式,这一过程是通过UI后端模块完成;
页面渲染优化
HTML文档结构层次尽量少,最好不深于六层; 脚本尽量后放,放在前即可; 少量首屏样式内联放在标签内; 样式结构层次尽量简单; 在脚本中尽量减少DOM操作,尽量缓存访问DOM的样式信息,避免过度触发回流; 减少通过JavaScript代码修改元素样式,尽量使用修改class名方式操作样式或动画; 动画尽量使用在绝对定位或固定定位的元素上; 隐藏在屏幕外,或在页面滚动时,尽量停止动画; 尽量缓存DOM查找,查找器尽量简洁; 涉及多域名的网站,可以开启域名预解析
推荐阅读:
七个开源的 SpringBoot 前后端分离项目,Star过千,快去收藏夹吃灰吧!
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
朕已阅
评论