从输入URL到页面返回都需要经过什么


从输入URL到页面返回都需要经过什么

  • DNS域名解析
  • TCP三次握手
  • 发送HTTP请求
  • 服务器处理请求并返回HTTP响应
  • 浏览器解析渲染页面
  • 断开连接

DNS域名解析

浏览器如何通过域名查询URL对应的IP地址

  • 浏览器缓存:浏览器搜索自己的 DNS 缓存
  • 操作系统缓存:搜索操作系统中的 DNS 缓存(内存中)
  • hosts:搜索操作系统的 hosts 文件
  • DNS服务器:操作系统将域名发送至 DNS服务器
    • LDNS:默认情况下,操作系统会将域名发送至LDMS(本地域名服务器),LDNS会查询自己的DNS缓存,查找成功则返回结果,失败则发起一个迭代DNS解析请求
    • LDNS 向 Root Name Server (根域名服务器,其虽然没有每个域名的的具体信息,但存储了负责每个域,如 com、net、org等的解析的顶级域名服务器的地址)发起请求,此处,Root Name Server 返回 com 域的顶级域名服务器的地址
    • LDNS 向 com 域的顶级域名服务器发起请求,返回 google.com 域名服务器地址
    • LDNS 向 google.com 域名服务器发起请求,得到 www.google.com 的 IP 地址
  • LDNS 将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来
  • 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来
  • 浏览器已经得到了域名对应的 IP 地址

TCP三次握手

第一次握手

建立连接时,客户端选择一个序号发送SYN包(SEQ=x)到服务器,并进入SYN_SEND状态,等待服务器确认

第二次握手

服务器收到SYN包,回应一个ACK段作为对x(ACK=x)的回应,同时宣告它自己的序号为y(SEQ=y),也就是SYN+ACK包,即ACK(SEQ=y,ACK=x),这个时候服务器进入SYN_RECV状态

第三次握手

客户端收到服务器ACK包,并在它发送的第一个数据段中(SEQ=x),对服务器选择的序号(ACK=y)进行确认,即DATA(SEQ=x,ACK=y)。之后客户端和服务器进入ESTABLISHED(TCP连接成功状态),完成三次握手。

SEQ: Sequence number 序号

ACK: Acknowledgement number 确认号

SYN: Synchronize Sequence Numbers 同步序列编号

发送HTTP请求

客户端发送一个HTTP请求到服务器的请求消息包括以下格式

  • 请求行:请求方法 URL 协议/版本 回车符和换行符(例:POST /admin/auth/login HTTP/1.1)

  • 请求头部:头部字段名:值 回车符和换行符(例:Accept-Encoding:gzip,deflate)

  • 空行: 回车符和换行符

  • 请求数据: (例:{“account”:”admin”,”password”:”admin”})

    完整例子

  POST /admin/auth/login HTTP/1.1
  Host: www.test.com
  Connection: keep-alive
  Content-Length: 38
  Accept: application/json, text/plain, */*
  DNT: 1
  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36
  Content-Type: application/json;charset=UTF-8
  Accept-Encoding: gzip, deflate
  Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8

  {"account":"admin","password":"admin"}

服务器处理请求并返回HTTP响应

正向代理

image_1b08aqr1s1ef3me7c56p061sj31g.png-9.3kB

如上图,因为google被墙,我们需要vpn翻墙才能访问google.com。

vpn对于“我们”来说,是可以感知到的(我们连接vpn)
vpn对于”google服务器”来说,是不可感知的(google只知道有http请求过来)。

对于人来说可以感知到,但服务器感知不到的服务器,我们叫他正向代理服务器。

反向代理

image_1b08aq6gvlbqnfp13k21odl1pr713.png-18.7kB

如上图,我们访问baidu.com的时候,baidu有一个代理服务器,通过这个代理服务器,可以做负载均衡,路由到不同的server。

此代理服务器,对于“我们”来说是不可感知的(我们只能感知到访问的是百度的服务器,不知道中间还有代理服务器来做负载均衡)。

此代理服务器,对于”server1 server2 server3″是可感知的(代理服务器负载均衡路由到不同的server)
对于人来说不可感知,但对于服务器来说是可以感知的,我们叫他反向代理服务器

Nginx

Nginx是什么

Nginx (“engine x”) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。

Php-fpm是什么

cgi、fast-cgi协议

cgi的历史

早期的webserver只处理html等静态文件,但是随着技术的发展,出现了像php等动态语言。
webserver处理不了了,怎么办呢?那就交给php解释器来处理吧!
交给php解释器处理很好,但是,php解释器如何与webserver进行通信呢?

为了解决不同的语言解释器(如php、python解释器)与webserver的通信,于是出现了cgi协议。只要你按照cgi协议去编写程序,就能实现语言解释器与webwerver的通信。如php-cgi程序。

fast-cgi的改进

有了cgi协议,解决了php解释器与webserver通信的问题,webserver终于可以处理动态语言了。
但是,webserver每收到一个请求,都会去fork一个cgi进程,请求结束再kill掉这个进程。这样有10000个请求,就需要fork、kill php-cgi进程10000次。

有没有发现很浪费资源?

于是,出现了cgi的改良版本,fast-cgi。fast-cgi每次处理完请求后,不会kill掉这个进程,而是保留这个进程,使这个进程可以一次处理多个请求。这样每次就不用重新fork一个进程了,大大提高了效率。

2、php-fpm是什么

php-fpm即php-Fastcgi Process Manager.
php-fpm是 FastCGI 的实现,并提供了进程管理的功能。
进程包含 master 进程和 worker 进程两种进程。
master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

Nginx如何与Php-fpm结合

当我们访问www.example.com的时候,处理流程是这样的:

www.example.com

-> Nginx

-> 路由到www.example.com/index.php

-> 加载nginx的fast-cgi模块

-> fast-cgi监听127.0.0.1:9000地址

-> www.example.com/index.php请求到达127.0.0.1:9000

-> php-fpm 监听127.0.0.1:9000

-> php-fpm 接收到请求,启用worker进程处理请求

-> php-fpm 处理完请求,返回给nginx

-> nginx将结果通过http返回给浏览器

服务器返回一个 HTTP 响应 

经过前面的几个步骤,服务器收到了我们的请求,也处理我们的请求,到这一步,它会把它的处理结果返回,也就是返回一个HTPP响应。 HTTP响应与HTTP请求相似,HTTP响应也由3个部分构成,分别是:

  • 状态行
  • 响应头(Response Header)
  • 空行
  • 响应正文
HTTP/1.1 200 OK
Server: nginx/1.13.12
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/7.1.20
Cache-Control: no-cache, private
Date: Tue, 20 Aug 2019 07:42:25 GMT

<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="csrf-token" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>seakee-admin</title>
    <!-- Styles -->
    <link href="/css/app.css" rel="stylesheet">
            <link href="/css/element-ui/2.10.1/index.css" rel="stylesheet">
        <link href="/css/font-awesome/5.9.0/all.min.css" rel="stylesheet">
    </head>
<body>
<div id="app"></div>
<script src="/js/app.js"></script>
</body>
</html>

浏览器解析渲染页面

浏览器是一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。

当文档加载过程中遇到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中js文件加载完毕,还要等待解析执行完毕,才可以恢复html文档的渲染线程。因为JS有可能会修改DOM,最为经典的document.write,这意味着,在JS执行完成前,后续所有资源的下载可能是没有必要的,这是js阻塞后续资源下载的根本原因。所以我明平时的代码中,js是放在html文档末尾的。

### 断开连接

现在的页面为了优化请求的耗时,默认都会开启持久连接(keep-alive),那么一个TCP连接确切关闭的时机,是这个tab标签页关闭的时候。这个关闭的过程就是著名的四次挥手。关闭是一个全双工的过程,发包的顺序的不一定的。一般来说是客户端主动发起的关闭,过程如下。

对于一个已经建立的连接,TCP使用改进的三次握手来释放连接(使用一个带有FIN附加标记的报文段)。TCP关闭连接的步骤如下:

第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。

第二步,主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。

第三步,主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。

第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。

发表评论

电子邮件地址不会被公开。 必填项已用*标注