写在前面


分析http的请求处理过程能够帮助读者更加深入的理解web服务架构的理解,为以后提升打下良好的基础,以下过程为本人在学习完lamp架构后的总结,如有错误,敬请指正。wKioL1nkBtegqRdtAAD1yuQt-S8463.png

请求处理过程:

域名解析--> 建立连接--> 接收请求--> 处理请求 --> 访问资源 --> 构建响应报文--> 发送响应报文  --> 记录日志

 

域名解析


假设用户在浏览器地址栏中输入http://yangzhiheng.blog.51cto.com发起一个请求。首先会把该域名(准确叫法为完全限定域名或者主机名)解析为ip地址。那么如何解析呢?

解析顺序:

检查浏览器自身DNS缓存—>  操作系统DNS缓存 –>hosts文件–>DNS解析

  1. 检查浏览器自身的DNS缓存,看自身缓存中是否有该FQDN的对应条目如果没有过期,则解析到所对应的ip地址。若没有该条目或者条目已过期则执行第2步。谷歌浏览器中查看缓存具体细节可在地址栏中输入chrome://net-internals/#dns来查看

  2.  检查操作系统自身所对应的DNS缓存是否有所对应的条目,对于linux主机,系统默认不对DNS进行缓存。如果在操作系统的dns缓存中找到未过期的所对应的条目,则解析到此结束。否则执行下一步。Windows系统查看系统dns缓存可以使用ipconfig /displaydns命令。

  3.  检查hosts文件中是否有该FQDN所对应的条目,如果有则解析成功,没有则进行下一步。在Windows系统中hosts文件位于C:\Windows\Sytem32\drivers\etc\目录下。linux系统中位于/etc/hosts目录下。

  4. 如果hosts文件中也不存在该域名所对应的条目,浏览器会向本地的DNS服务器发起一个域名解析请求。域名解析请求使用udp的53端口,本地DNS请求为递归请求。本地的DNS服务器会先检查自身的缓存,是否有该FQDN所对应的条目。如果有切且没有过期则返回给浏览器,解析成功。如果没有找到对应条目则本地DNS服务器发起一个迭代的DNS解析请求,首先向根域名服务器查找,根域名服务器在全球有13个。如果根域名服务器中存储的是顶级域名所对应的DNS服务器的ip地址。以yangzhiheng.blog.51cto.com为例,根域名返回给本地DNS服务器一个com域所对应的DNS服务器地址,本地DNS服务器向com域名服务器请求该FQDN请求,com域名服务器则返回给本地服务器一个51cto域的DNS服务器,这样层层迭代,最终返回给本地DNS一个正确的ip地址。本地服务器得到ip地址后缓存下来,并返回给浏览器。浏览器根据该ip地址来进行访问。

DNS 的详细解析过程,读者可以参考http://vinsent.blog.51cto.com/13116656/1967876该文章来加深理解。


建立连接


得到IP地址后,浏览器会开启一个随机端口向web服务器的80端口发起tcp链接请求,经过3次握手后建立tcp连接,然后浏览器发起httpd请求。

建立连接过程:

三次握手  --> 发起http请求

三次握手过程详解

 

wKiom1nkCW-C7CjrAADns2zTVnQ666.png

  1.  假定客户端A为有一个浏览器(TCP客户端程序),服务器B运行httpd服务程序(TCP服务器程序)。最初两端的tcp进程都处于一种closed状态。客户端A为主动打开连接(开启随机端口,并创建传输控制块),服务器B被动打开连接(启动httpd服务,使80端口为监听状态,准备接受客户端进程的连接请求)。

  2. 服务器B收到连接请求报文后,如果同意与客户端A建立连接则向A发送确认。在确认报文中SYNACK位都置1,确认号ack = x+1,同时也为自己选择一个初始序号seq=y。该报文也不携带数据,但也要消耗一个序号。这时服务B进入SYN-RCVD(同步收到)状态。表示服务器B已经收到客户端A的连接请求,等待客户端A的确认。

  3. 客户端进程收到B的确认后,还要向B给出确认。确认报文中的ACK1,确认号ack=y+1,表示期望收到第y+1个报文。自己的报文序号seq=x+1TCP协议规定该ACK的确认包可以携带,但是如果不携带数据就不消耗序列号。也就是说这个报文发出去后,如果不携带数据则下一个报文发送该序列号仍是seq=x+1。这时tcp连接以建立。客户端进入ESTABLISHED状态。当服务器B收到该确认报文后,也进入ESTABLISHED状态。

TCP为什么要进行三次握手?

  在日常生活中,连接的建立只需两次握手应该就可以了,客户端发送一个请求,服务器端发送一个确认报文就可以建立连接了。但是为什么客户端A为什么还要再一次发送确认报文呢?主要是为了防止已失效的连接请求报文段突然又传送到了服务器B,因而产生错误。

 已失效的连接请求报文段是这样产生的,比如客户端A发送了一个连接请求报文由于某些网络原因在某个网络节点滞留了,客户端A误以为该数据包丢失,然后又发送一个连接请求报文,该报文正常到达后并数据传输完成后滞留的报文又传送至服务器B。本来一个这就是一个早已失效的报文,但是B收到此报文后,就误认为客户端A又发送了一次连接请求。假定不采用三次握手,服务器B发出确认后,新的连接就建立了,所以为了防止上次情况的发生,就有了三次握手来建立连接。

 

发起http请求:

tcp建立连接之后,客户端发起http请求。请求报文格式如下 

起始行:请求方法  请求的URL 请求的协议版本

头部信息:user-agent  host等键值对

主体

无论请求报文还是响应报文都遵循该报文格式,其中请求方法一般有GET,POST,HEAD,PUT,DELETE,TRACE,OPTION等URL为同一资源标识符,用于描述服务器某个特定资源的位置,组成:scheme://Server:Port/path/to/resource。协议版本尝试用http/1.1版本。头部信息一般为host后面以冒号分隔要请求的主机名。

接收请求


  接收请求所要完成的工作就是接收来自网络的请求报文中对某一资源的一次请求过程。其接收请求的模型包括以下几类:

单进程I/O模型:启动一个进程处理用户请求,而且一次只处理一个,多个请求被串行响应。

多进程I/O结构:并行启动多个进程,每个进程处理一个请求。

复用I/O结构:一个进程响应n个请求。

       多线程模型:一个进程生成n个线程,每个线程响应一个用户请求。

       事件驱动:event-driven

                                  

复用的多线程I/O结构:启动多个m进程,每个进程响应n个请求。

处理请求


  对请求报文进行分析,并获取请求的资源及请求方法等相关信息。以Apacheprefork工作模式为例,其管理进程在接收到请求报文后会选择一个工作进程来对该请求进行处理,得到其请求的方法。和资源的URL等相关信息。

访问资源


  在对请求处理时一般需要访问后端资源,以lamp架构为例,Apache工作进程把请求转发到PHP-fpm管理进程(默认端口为9000),PHP-FPM管理进程分配一个工作进程用于处理index.php请求,工作进程在服务器路径中找到index.php文件,进行解析,编译。然后执行编译后的php代码,这时一般还要访问后端的数据库服务器等。得到请求的结果,把结果返回给apache服务器。

构建响应报文


  Apache在得到返回的请求结果后,开始构建响应报文。响应报文中包含有响应状态码、响应首部,如果生成了响应主体的话,还包括响应主体。这时还有一个较为重要的要点,就是资源访问重定向问题。

web服务构建的响应并非客户端请求的资源,而是资源的另外一个访问路径:可分为永久重定向和临时重定向。

永久重定向:curl -I http://www.360buy.com 得到的结果 HTTP/1.1 301 Moved Permanently 

临时重定向:curl -I http://www.taobao.com 得到的结果  HTTP/1.1 302 Found

发送响应报文


响应报文构建完成之后,发送响应报文

记录日志


最后,当事务结束时,Web服务器会在日志文件中添加一个条目,来描述已执行的事务