geekyouth / geekyouth.github.io

👣极客青年博客😘,基于github pages+issues + VUE 2.0 框架构建的轻量级静态博客系统💎[速度慢请翻墙]
https://java666.cn
39 stars 6 forks source link

从URL开始,定位世界|从我们输入 URL 并按下回车键到看到网页结果之间发生了什么? #12

Closed geekyouth closed 2 years ago

geekyouth commented 6 years ago

原文地址 http://www.wangtianyi.top/blog/2017/10/22/cong-urlkai-shi-,ding-wei-shi-jie/

从我们输入 URL 并按下回车键到看到网页结果之间发生了什么?换句话说,一张网页,要经历怎样的过程,才能抵达用户面前?下面来从一些细节上面尝试一下探寻里面的秘密。

前言:键盘与硬件中断

说到输入 URL,当然是从手敲键盘开始。对于键盘,生活中用到的最常见的键盘有两种:薄膜键盘、机械键盘。

键盘传输信号到操作系统后便会触发硬件中断处理程序硬件中断是操作系统中提高系统效率、满足实时需求的非常重要的信号处理机制,它是一个异步信号,并提供相关中断的注册表(IDT)与请求线(IRQ)。键盘被按压时,将通过请求线将信号输入给操作系统,CPU 在当前指令结束之后,根据注册表与信号响应该中断并利用段寄存器装入中断程序入口地址。具体可参看操作系统与汇编相关书籍。

当然本文主要不是介绍硬件与操作系统中的细节,前言只是想说明,从输入 URL 到浏览器展现结果页面之间有太多底层相关的知识,怀着一颗敬畏的心并且在有限的篇幅中是无法详细阐述的,所以本文会将关注点放在在一个稍高的角度上来看,从浏览器替我们发送请求之后到看到页面显示完成这中间中发生了什么事情,比如 DNS 解析、浏览器渲染。

浏览器解析 URL

按下回车键之前

比如我按下一个‘b’键,会出现很多待选 URL 给我,第一个便是百度。那么其实是在浏览器接收到这个消息之后,会触发浏览器的自动完成机制,会在你之前访问过的搜索最匹配的相关 URL,会根据特定的算法显示出来供用户选择。

按下回车键之后

依据上述键盘触发原理,一个专用于回车键的电流回路通过不同的方式闭合了。然后触发硬件中断,随之操作系统内核去处理对应中断。省略其中的过程,最后交给了浏览器这样一个 “回车” 信号。那么浏览器(本文涉及到的浏览器版本都为 Chrome 61)会进行以下但不仅限于以下炫酷(乱七八糟)的步骤:

  1. 解析 URL:您输入的是 http 还是 https 开头的网络资源 / file 开头的文件资源 / 待搜索的关键字?然后浏览器进行对应的资源加载进程
  2. URL 转码:RFC 标准中规定部分字符可以不经过转码直接用于 URL,但是汉字不在范围内。所以如果在网址路径中包含汉字将会被转码,比如 https://zh.wikipedia.org/wiki/HTTP%E4%B8%A5%E6%A0%BC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8 转换成 https://zh.wikipedia.org/wiki/HTTP%E4%B8%A5%E6%A0%BC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8
  3. HSTS:鉴于 HTTPS 遗留的安全隐患,大部分现代浏览器已经支持 HSTS。对于浏览器来说,浏览器会检测是否该网络资源存在于预设定的只使用 HTTPS 的网站列表,或者是否保存过以前访问过的只能使用 HTTPS 的网站记录,如果是,浏览器将强行使用 HTTPS 方式访问该网站。

DNS 解析

不查 DNS,读取缓存

发送 DNS 查找请求

DNS 的查询方式是:按根域名 -> 顶级域名 -> 次级域名 -> 主机名这样的方式来查找的,对于某个 URL,如下所示

主机名.次级域名.顶级域名.根域名
--------------------------
host.sld.tld.root

查询步骤为:

  1. 查询本地 DNS 服务器。本地 DNS 服务器地址为连接网络时路由器指定的 DNS 地址,一般为 DHCP 自动分配的路由器地址,保存在 / etc/resolv.conf,而路由器的 DNS 转发器将请求转发到上层 ISP 的 DNS,所以此处本地 DNS 服务器是局域网或者运营商的。
  2. 从 "根域名服务器" 查到 "顶级域名服务器" 的 NS 记录和 A 记录(IP 地址)。世界上一共有十三组根域名服务器,从 A.ROOT-SERVERS.NET 一直到 M.ROOT-SERVERS.NET,由于已经将这些根域名服务器的 IP 地址存放在本地 DNS 服务器中。
  3. 从 "顶级域名服务器" 查到 "次级域名服务器" 的 NS 记录和 A 记录(IP 地址)
  4. 从 "次级域名服务器" 查出 "主机名" 的 IP 地址

以 www.google.com 为例,下面是一整个 DNS 查询过程:

  1. 由于本次测试是在阿里云上的实例进行测试,所以首先从 100.100.2.138 这个阿里内网 DNS 服务器查找到所有根域名服务器的映射关系。
  2. 访问根域名服务器(f.root-servers.net),拿到 com 顶级域名服务器的 NS 记录与 IP 地址。
  3. 访问顶级域名服务器(e.gtld-servers.net),拿到 google.com 次级域名服务器的 NS 记录与 IP 地址。
  4. 访问次级域名服务器(ns2.google.com),拿到 www.google.com 的 IP 地址
.         388687  IN  NS  m.root-servers.net.
.         388687  IN  NS  d.root-servers.net.
.         388687  IN  NS  h.root-servers.net.
.         388687  IN  NS  l.root-servers.net.
.         388687  IN  NS  c.root-servers.net.
.         388687  IN  NS  g.root-servers.net.
.         388687  IN  NS  a.root-servers.net.
.         388687  IN  NS  k.root-servers.net.
.         388687  IN  NS  i.root-servers.net.
.         388687  IN  NS  e.root-servers.net.
.         388687  IN  NS  f.root-servers.net.
.         388687  IN  NS  j.root-servers.net.
.         388687  IN  NS  b.root-servers.net.
;; Received 1097 bytes from 100.100.2.138#53(100.100.2.138) in 1072 ms

com.          172800  IN  NS  l.gtld-servers.net.
com.          172800  IN  NS  a.gtld-servers.net.
com.          172800  IN  NS  i.gtld-servers.net.
com.          172800  IN  NS  j.gtld-servers.net.
com.          172800  IN  NS  b.gtld-servers.net.
com.          172800  IN  NS  f.gtld-servers.net.
com.          172800  IN  NS  m.gtld-servers.net.
com.          172800  IN  NS  h.gtld-servers.net.
com.          172800  IN  NS  e.gtld-servers.net.
com.          172800  IN  NS  g.gtld-servers.net.
com.          172800  IN  NS  c.gtld-servers.net.
com.          172800  IN  NS  k.gtld-servers.net.
com.          172800  IN  NS  d.gtld-servers.net.
;; Received 1174 bytes from 192.5.5.241#53(f.root-servers.net) in 1841 ms

google.com.       172800  IN  NS  ns2.google.com.
google.com.       172800  IN  NS  ns1.google.com.
google.com.       172800  IN  NS  ns3.google.com.
google.com.       172800  IN  NS  ns4.google.com.
;; Received 664 bytes from 192.12.94.30#53(e.gtld-servers.net) in 185 ms

www.google.com.       300 IN  A   216.58.200.228
;; Received 48 bytes from 216.239.34.10#53(ns2.google.com) in 2 ms

所以总的来说,DNS 的解析是一个逐步缩小范围的查找过程。

建立 HTTPS、TCP 连接

确定发送目标

拿到 IP 之后,还需要拿到那台服务器的 MAC 地址才行,在以太网协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的 MAC 地址。所以根据ARP(根据 IP 地址获取物理地址的一个 TCP/IP 协议)获取到 MAC 地址之后保存到本地 ARP 缓存之后与目标主机准备开始通信。具体细节参见维基百科 DHCH/ARP。

建立 TCP 连接

为什么握手一定要是三次?

这样就保证了A 与 B 之间既能相互发送请求也能相互接收解析请求。同时避免了因为网络延迟产生的重复连接问题,比如 A 发送一次连接请求但网络延迟导致这次请求是在 A 重发连接请求并完成与 B 通信之后的,有三次握手的话,B 返回的建立请求 A 就不会理睬了。

短连接与长连接?

上图是一个短连接的过程演示,对于长连接,A 与 B 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。另外,由于长连接的实现比较困难,需要要求长连接在没有数据通信时,定时发送数据包 (心跳),以维持连接状态,并且长连接对于服务器的压力也会很大,所以推送服务对于一般的开发者是非常难以实现的,这样的话就出现了很多不同的大型厂商提供的消息推送服务。

进行 TLS 加密过程

服务端的处理

静态缓存、CDN

为了优化网站访问速度并减少服务器压力,通常将 html、js、css、文件这样的静态文件放在独立的缓存服务器或者部署在类似 Amazon CloudFront 的 CDN 云服务上,然后根据缓存过期配置确定本次访问是否会请求源服务器来更新缓存。

负载均衡

负载均衡具体实现有多种,有直接基于硬件的 F5,有操作系统传输层 (TCP) 上的 LVS,也有在应用层 (HTTP) 实现的反向代理(也叫七层代理),下面简单介绍一下最后者。

在请求发送到真正处理请求的服务器之前,还需要将请求路由到适合的服务器上,一个请求被负载均衡器拿到之后,需要做一些处理,比如压缩请求(在 nginx 中 gzip 压缩格式是默认配置在 nginx.conf 内的,所以默认开启,如果不对数据量要求特别精细的话,默认配置完全可以满足基本需求)、接收请求(接收完毕后才发给 Server,提高 Server 处理效率),然后根据预定的路由算法,将此次请求发送到某个后台服务器上。

其中需要提到的一点是反向代理,先回顾一下反向代理的原理: 正向代理是将自己要访问的资源告诉 Proxy,让 Proxy 帮你拿到数据返回给你,Proxy 服务于 Client,常用于翻墙和跨权限操作; 反向代理也是将自己要访问的资源告诉 Proxy,让 Proxy 帮你拿到数据返回给你,但是 Proxy 服务于 Server,它会将请求接受完毕之后发送给某一合适的 Server,这个时候 Client 是不知道是根据什么规则并且也不知道最后是哪一个 Server 服务于它的,所以叫反向代理,常用于负载均衡、安全控制.

服务器的处理

对于 HTTPD(HTTP Daemon) 在服务器上部署,最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx。对于 Java 平台来说,Tomcat 是 Spring Boot 也会默认选用的 Servlet 容器实现,Tomcat 对于请求的处理如下:

  1. 请求到达 Tomcat 启动时监听的 TCP 端口。
  2. 解析请求中的各种信息之后创建一个 Request 对象并填充那些有可能被所引用的 Servlet 使用的信息,如参数,头部、cookies、查询字符串等。
  3. 创建一个 Response 对象,所引用的 Servlet 使用它来给客户端发送响应。
  4. 调用 Servlet 的 service 方法,并传入 Request 和 Response 对象。这里 Servlet 会从 Request 对象取值,给 Response 写值。
  5. 根据我们自己写的 Servlet 程序或者框架携带的 Servlet 类做进一步的处理(业务处理、请求的进一步处理)
  6. 最后根据 Servlet 返回的 Response 生成相应的 HTTP 响应报文。

浏览器的渲染

浏览器的功能是从服务器上取回你想要的资源,然后展示在浏览器窗口当中。资源通常是 HTML 文件,也可能是 PDF,图片,或者其他类型的内容。也可以显示其他类型的插件 (浏览器扩展)。例如显示 PDF 使用 PDF 浏览器插件。资源的位置通过用户提供的 URI(Uniform Resource Identifier) 来确定。

浏览器解释和展示 HTML 文件的方法,在 HTML 和 CSS 的标准中有详细介绍。这些标准由 Web 标准组织 W3C(World Wide Web Consortium) 维护。

下面会以 Chrome 中使用的浏览器引擎 Webkit 为例,根据上图来简单介绍浏览器的渲染。具体解析、渲染会涉及到非常多的细节,请参考 HTML5 渲染规范和对应的页面 GPU 渲染实现。

HTML 解析

浏览器拿到具体的 HTML 文档之后,需要调用浏览器中使用的浏览器引擎中处理 HTML 的工具(HTML Parser)来将 HTML 文档解析成为DOM 树,将以便外部接口(JS)调用。

CSS 解析

  • 根据 CSS 词法和句法分析 CSS 文件和