[原理] 说说Web请求的过程

在面试过程中,我们可能都遇到过这个问题,如果我们不是很理解整个过程,可能觉得就是客户端发送请求,服务端处理请求这个简单的过程。但是在工作中,我发现对这个问题理解的深度能让你更快且精确的定位到的问题的所在,最起码能知道是不是我们服务端的问题,接下来小生姑且根据目前自己的理解和参考资料对过程进行梳理。

DNS解析域名

我们都知道Http/Https是基于TCP的应用层协议,他们其实对主机是不敏感的,但是其底层的TCP对主机是敏感的:TCP需要一个ip地址来唯一标识一个目的主机。我们在浏览器浏览时,通常都是输入www.xxx.com域名,我们需要的ip从何而来呢?这就要归功于DNS域名解析了。
DNS解析域名的原理其实就是建立了<域名, ip>的一个映射关系,这个映射关系可能保存在DNS服务器中,也可能在本地Hosts文件中。当你在浏览器输入www.baidu.com,主机会先对本地Hosts文件进行查询,如果存在映射,则直接使用Hosts文件中映射的ip地址;否则请求DNS服务器。一般后端都是使用nginx反向代理转发请求到集群,所以一般我们域名都是直接绑定nginx服务器,然后通过nginx管理集群列表。
在linux和unix下我们可以用dig utility来查询DNS的相关信息。

dig baidu.com

发起连接

三次握手

TCP的三次握手和四次挥手可以说是老生常谈的话题了,这里姑且罗列三次握手的过程:

  • 浏览器发起TCP连接,设置标识位syn,随机一个cliSeq
  • 服务器接收到TCP连接报文,服务器的响应报文同样设置syn位,ack cliSeq + 1,服务随机一个serverSeq
  • 浏览器发送报文,ack serSeq + 1,不需要设置syn,可携带数据

三次握手也涉及了TCP的很多关键知识,seq:缓存乱序报文、进行报文排序、防重放攻击等,ack:确认和重发实现的可靠传输连接,具体内容参考:《计算机网络:自顶向下方法》第三章 运输层
如果是Https,还需要在三次握手之后协商ssl/tls会话参数,再次不详述。

发送HTTP请求

建立TCP连接之后,发送Http报文。

路由转发和路由选择

报文从客户端主机发送出来后,是怎么来到服务端主机的呢?这部分就是要说明这个问题。

我们每个ip都隶属于一个子网,如果客户机和服务主机都是在同一子网内,直接在子网内广播即可。但是,更多的是两个ip不是同一子网的情况,这就需要通过路由器进行子网之间的转发。

在这里插入图片描述

我们可以看到,路由转发是通过一个转发表实现的。那怎么得到转发表呢?静态配置或者自学习。静态配置就是通过路由器后台将子网掩码绑定到某个接口上。自学习是路由器会自动记录通过该路由器的ip报文的<ip报文源主机, 接口来源>。

路由选择就是选择转发目前ip地址的下一跳的地址,网络拓扑结构建模之后就是一个带权无向图,所以图的最短路径算法在网络拓扑中就是一个应用。路由选择算法按照是否需要全局路由信息分为分布式和集中式,分布式就是每台路由器监听相邻路由器的路径变化,每次变化运行一次Dijstra算法,当相邻路由器路径没有变化时,算法结束;集中式的路由选择算法要求每台路由器广播并收集所有的路由表信息,然后运行最短路径算法进行计算。具体内容参见:《计算机网络:自顶向下方法》第四章 网络层

Nginx反向代理

首先我们需要理解什么是代理?假设我们要在北京租个小单间,按照传统的方式,我们(客户端)需要找到房东(服务器),然后租房(执行),得到一间单间。而现在呢?很多的房间信息都会托管到一个中介平台(代理)上,在比如自如这种中介平台(Nginx代理)上,我们租房只需要跟中介平台进行沟通签订合同然后得到一间单间这个结果。是否感觉其实代理过程也差不多?其实不然,我们提到“很多的房间信息都会托管到一个中介平台(代理)上”,这些房间类似于服务器,集群都放到代理中统一管理,能更好地做负载均衡、请求转发等操作。

代理分为反向代理和正向代理,其实这个是相对于客户端来说的,如果客户端需要配置代理服务器(比如VPN),客户端同时知道真实的服务器地址,这就是正向代理,而反向代理,客户端只知道代理服务器,只需要访问代理服务器就能拿到结果。

在这里插入图片描述

当我们拿到一个域名www.abc.com时,如果服务端为集群时,其实这个域名是nginx服务器的域名,只是通过nginx将请求负载均衡到不同主机上。

服务器处理请求

解析Http请求

解析Http请求就是服务器的事情了,

跨域请求检测和处理

首先我们需要理解跨域的含义,我们参考Spring基于https://www.w3.org/TR/cors/实现的CorsUtils#isCorRequest,跨域请求其实就是请求中Origin首部携带的url中和当前请求的主机不匹配(schema、host、port有一个或一个以上不匹配),比如Origin: http://baidu.com,我们调用的api为:http://github.com/api/test,显然http://github.comhttp://baidu.com,还需要注意的是schema不同也属于跨域,比如https://baidu.comhttp://baidu.com。还有一种比较容易忽略的情况就是通过ip访问和通过域名访问这种情况也会判定为跨域。

在这里插入图片描述

我们首先需要认识几个关于CORS的首部:
客户端请求携带:

  • Origin
    表明请求来源,通常就是页面所在的Host

服务端响应携带:

  • Access-Control-Allow-Origin
    返回服务器支持的Origin。服务端可能返回Access-Control-Allow-Origin: *或者Access-Control-Allow-Origin: {allowCliOrigin}
  • Access-Control-Request-Method
    返回被允许的访问控制的请求方法,在预请求(Preflight)中必须返回
  • Access-Control-Request-Headers
    返回被允许的访问控制的请求首部,在预请求(Preflight)中必须返回
  • Access-Control-Allow-Credentials
    Credentitials是指一些凭证信息,我们最常用的就是Cookie,如果我们需要携带Cookie,前端请求时必须设置xhr的withCredentials=true,此时服务端响应中必须包含Access-Control-Allow-Credentials: true

如果客户端没有设置withCredentials=true,浏览器将不会携带Cookie;服务端响应中不包含Access-Control-Allow-Credentials: true,请求是成功的,但是浏览器会拦截结果,所以前端最终还是无法获取到结果。
在这里插入图片描述在这里插入图片描述
这里有几个关于跨域知识的比较权威的讲解的网站:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Server-Side_Access_Control
http://arunranga.com/examples/access-control/
https://www.w3.org/TR/cors/#simple-cross-origin-request

Spring中的CorsFilter其实是参考w3c标准实现的,可以学习使用。

选择处理方法并响应

Spring DispatcherServlet#doDispatch逻辑,详见:https://blog.csdn.net/lidelin10/article/details/100033302

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页