一个 http 请求的整个流程
那么在分布式架构中,有一个很重要的环节,就是分布式网络中的计算机节点彼此之间需要
通信。这个通信的过程一定会涉及到通信协议相关的知识点,当然大家也可能知道一些,但
是我会尽可能的把通信这一块的内容串起来,加深大家的理解。我们每天都在用浏览器访问各种网站,作为用户来说,只需要需要输入一个网址并且正确跳转就行。但是作为程序员,看到的可能就是这个响应背后的整体流程。所以我想通过一个 http请求的整个流程来进行讲解通信的知识
负责域名解析的 DNS 服务
首先,用户访问一个域名,会经过 DNS 解析
DNS(Domain Name System),它和 HTTP 协议一样是位于应用层的协议,主要提供域名到
IP 的解析服务。我们其实不用域名也可以访问目标主机的服务,但是 IP 本身不是那么容易
记,所以使用域名进行替换使得用户更容易记住。
加速静态内容访问速度的 CDN
我这里只讲了比较简单的解析流程,在很多大型网站,会引入 CDN 来加速静态内容的访问,
这里简单给大家解释一下什么是 CDN(Content Delivery Network),表示的是内容分发网
络。CDN 其实就是一种网络缓存技术,能够把一些相对稳定的资源放到距离最终用户较近的
地方,一方面可以节省整个广域网的带宽消耗,另外一方面可以提升用户的访问速度,改进
用户体验。我们一般会把静态的文件(图片、脚本、静态页面)放到 CDN 中。
如果引入了 CDN,那么解析的流程可能会稍微复杂一点,大家有空自己去了解一下。比如阿
里云就提供了 cdn 的功能。
HTTP 协议通信原理
域名被成功解析以后,客户端和服务端之间,是怎么建立连接并且如何通信的呢?
说到通信,大家一定听过 tcp 和 udp 这两种通信协议,以及建立连接的握手过程。而 http 协议的通信是基于 tcp/ip 协议之上的一个应用层协议,应用层协议除了 http 还有哪些呢(FTP、DNS、SMTP、Telnet 等)。
涉及到网络协议,我们一定需要知道 OSI 七层网络模型和 TCP/IP 四层概念模型,OSI 七层网络模型包含(应用层、表示层、会话层、传输层、网络层、数据链路层、物理层)、TCP/IP 四层概念模型包含(应用层、传输层、网络层、数据链路层)。
请求发起过程,在 tcp/ip 四层网络模型中所做的事情
当应用程序用 T C P 传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一
串比特流送入网络。其中每一层对收到的数据都要增加一些首部信息(有时还要增加尾部信
息)
客户端如何找到目标服务
在客户端发起请求的时候,我们会在数据链路层去组装目标机器的 MAC 地址,目标机器的
mac 地址怎么得到呢? 这里就涉及到一个 ARP 协议,这个协议简单来说就是已知目标机器
的 ip,需要获得目标机器的 mac 地址。(发送一个广播消息,这个 ip 是谁的,请来认领。认
领 ip 的机器会发送一个 mac 地址的响应)
有了这个目标 MAC 地址,数据包在链路上广播,MAC 的网卡才能发现,这个包是给它的。
MAC 的网卡把包收进来,然后打开 IP 包,发现 IP 地址也是自己的,再打开 TCP 包,发
现端口是自己,也就是 80 端口,而这个时候这台机器上有一个 nginx 是监听 80 端口。
于是将请求提交给 nginx,nginx 返回一个网页。然后将网页需要发回请求的机器。然后层层封装,最后到 MAC 层。因为来的时候有源 MAC 地址,返回的时候,源 MAC 就变成了目标 MAC,再返给请求的机器。
为了避免每次都用 ARP 请求,机器本地也会进行 ARP 缓存。当然机器会不断地上线下线,IP 也可能会变,所以 ARP 的 MAC 地址缓存过一段时间就会过期
接收端收到数据包以后的处理过程
当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协
议加上的报文首部。每层协议都要去检查报文首部中的协议标识,以确定接收数据的上层协
议。
为什么有了 MAC 层还要走 IP 层呢?
之前我们提到,mac 地址是唯一的,那理论上,在任何两个设备之间,我应该都可以通过
mac 地址发送数据,为什么还需要 ip 地址?
mac 地址就好像个人的身份证号,人的身份证号和人户口所在的城市,出生的日期有关,
但是和人所在的位置没有关系,人是会移动的,知道一个人的身份证号,并不能找到它这个
人,mac 地址类似,它是和设备的生产者,批次,日期之类的关联起来,知道一个设备的
mac,并不能在网络中将数据发送给它,除非它和发送方的在同一个网络内。
所以要实现机器之间的通信,我们还需要有 ip 地址的概念,ip 地址表达的是当前机器在网
络中的位置,类似于城市名+道路号+门牌号的概念。通过 ip 层的寻址,我们能知道按何种
路径在全世界任意两台 Internet 上的的机器间传输数据。
TCP/IP 的分层管理
TCP/IP 协议按照层次分为 4 层:应用层、传输层、网络层、数据链路层。对于分层这个概念,
大家一定不陌生,比如我们的分布式架构体系中会分为业务层、服务层、基础支撑层。比如
docker,也是基于分层来实现。所以我们会发现,复杂的程序都需要分层,这个是软件设计的要求,每一层专注于当前领域的事情。如果某些地方需要修改,我们只需要把变动的层替换掉就行,一方面改动影响较少,另一方面整个架构的灵活性也更高。 最后,在分层之后,整个架构的设计也变得相对简单了。
分层负载
了解了分层的概念以后,我们再去理解所谓的二层负载、三层负载、四层负载、七层负载就
容易多了。
一次 http 请求过来,一定会从应用层到传输层,完成整个交互。只要是在网络上跑的数据包,都是完整的。可以有下层没上层,绝对不可能有上层没下层。
二层负载
二层负载是针对 MAC,负载均衡服务器对外依然提供一个 VIP(虚 IP),集群中不同的机器采用相同 IP 地址,但是机器的 MAC 地址不一样。当负载均衡服务器接受到请求之后,通过改写报文的目标 MAC 地址的方式将请求转发到目标机器实现负载均衡
二层负载均衡会通过一个虚拟 MAC 地址接收请求,然后再分配到真实的 MAC 地址
三层负载均衡
三层负载是针对 IP,和二层负载均衡类似,负载均衡服务器对外依然提供一个 VIP(虚 IP),但是集群中不同的机器采用不同的 IP 地址。当负载均衡服务器接受到请求之后,根据不同的负载均衡算法,通过 IP 将请求转发至不同的真实服务器
三层负载均衡会通过一个虚拟 IP 地址接收请求,然后再分配到真实的 IP 地址
四层负载均衡
四层负载均衡工作在 OSI 模型的传输层,由于在传输层,只有 TCP/UDP 协议,这两种协议
中除了包含源 IP、目标 IP 以外,还包含源端口号及目的端口号。四层负载均衡服务器在接受到客户端请求后,以后通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器。
四层通过虚拟 IP + 端口接收请求,然后再分配到真实的服务器
七层负载均衡
七层负载均衡工作在 OSI 模型的应用层,应用层协议较多,常用 http、radius、dns 等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Web 服务器的负载均衡,除了根据 IP 加端口进行负载外,还可根据七层的 URL、浏览器类别来决定是否要进行负载均衡
七层通过虚拟的 URL 或主机名接收请求,然后再分配到真实的服务器。
TCP/IP 协议的深入分析
通过前面一个案例的分析,基本清楚了网络的通信流程,在 http 协议中,底层用到了 tcp 的
通信协议,我们接下来给大家简单介绍一下 tcp 的通信协议原理。
我们如果需要深入学习网络协议,就要先把一些基本的协议的作用和工作过程搞清楚,网络
设备还没智能到人脑的程度,它是由人类创造出来的,它的工作过程肯定是符合人类的交流
习惯并按照人类的交流习惯来设计的。所以要以人类的思维方式去理解这些协议。
例如,你给别人打电话,不可能电话一接通你就啪啦啪啦地说一大通,万一对方接通电话后
因为有事还没来得及倾听呢?这不太符合正常人类的交流习惯。一般是电话接通后,双方会
有个交互的过程,会先说一声“你好”,然后对方也回复一声“你好”,双方通过各自一句“你好”
明确对方的注意力都放在了电话沟通上,然后你们双方就可以开始交流了,这才是正常的人
类交流方式,这个过程体现在计算机网络里就是网络协议!我们通过 TCP 协议在两台电脑建立网络连接之前要先发数据包进行沟通,沟通后再建立连接,然后才是信息的传输。而 UDP协议就类似于我们的校园广播,广播内容已经通过广播站播放出去了,你能不能听到,那就与广播站无关了,正常情况下,不可能你说没注意听然后再让广播站再播放一次广播内容。
基于这些思路,我们先去了解下 TCP 里面关注比较多的握手协议
TCP connection
客户端与服务器之间数据的发送和返回的过程当中需要创建一个叫TCP connection的东西;
由于TCP不存在连接的概念,只存在请求和响应,请求和响应都是数据包,它们之间都是经过由TCP创建的一个从客户端发起,服务器接收的类似连接的通道,这个连接可以一直保持,http请求是在这个连接的基础上发送的;
在一个TCP连接上是可以发送多个http请求的,不同的版本这个模式不一样。
在HTTP/1.0中这个TCP连接是在http请求创建的时候同步创建的,http请求发送到服务器端,服务器端响应了之后,这个TCP连接就关闭了;
HTTP/1.1中可以以某种方式声明这个连接一直保持,一个请求传输完之后,另一个请求可以接着传输。这样的好处是:在创建一个TCP连接的过程中需要“三次握手”的消耗,“三次握手”代表有三次网络传输。
如果TCP连接保持,第二个请求发送就没有这“三次握手”的消耗。HTTP/2中同一个TCP连接里还可以并发地传输http请求。
TCP报文格式简介
其中比较重要的字段有:
(1)序号(sequence number):Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
(2)确认号(acknowledgement number):Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
(3)标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:
URG:紧急指针(urgent pointer)有效。
ACK:确认序号有效。
PSH:接收方应该尽快将这个报文交给应用层。
RST:重置连接。
SYN:发起一个新连接。
FIN:释放一个连接。
需要注意的是:
不要将确认序号Ack与标志位中的ACK搞混了。确认方Ack=发起方Seq+1,两端配对。
TCP的三次握手(Three-Way Handshake)
所以 TCP 消息的可靠性首先来自于有效的连接建立,所以在数据进行传输前,需要通过三次
握手建立一个连接,所谓的三次握手,就是在建立 TCP 链接时,需要客户端和服务端总共发
送 3 个包来确认连接的建立,在 socket 编程中,这个过程由客户端执行 connect 来触发
握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。随后开始“三次握手”:
(1)首先客户端向服务器端发送一段TCP报文,其中:
- 标记位为SYN,表示“请求建立新连接”;
- 序号为Seq=X(X一般为1);
- 随后客户端进入SYN-SENT阶段。
(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:
- 标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
- 序号为Seq=y;
- 确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值;随后服务器端进入SYN-RCVD阶段。
(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:
- 标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);
- 序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;
- 确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;
- 随后客户端进入ESTABLISHED阶段。
服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。
在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。
此后客户端和服务器端进行正常的数据传输。这就是“三次握手”的过程。
那 TCP 在三次握手的时候,IP 层和 MAC 层在做什么呢?当然是 TCP 发送每一个消息, 都会带着 IP 层和 MAC 层了。因为,TCP 每发送一个消息,IP 层和 MAC 层的所有机制都 要运行一遍。而你只看到 TCP 三次握手了,其实,IP 层和 MAC 层为此也忙活好久了。