精华内容
下载资源
问答
  • 一个Servlet请求的请求过程

    千次阅读 2018-10-21 15:44:06
    client 发起一个请求request,首先client发起DNS解析先拿到服务器的ip:通过解析本地host文件,如没有,则将域名发到上级DNS,一直到能解析出ip。 发起网络通信,建立连接:三次握手 发送报文,到服务器...

    首先看servlet的接口定义

    public interface Servlet {
        
          public void init(ServletConfig config) throws ServletException;
        
          public ServletConfig getServletConfig();
        
          public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
        
          public String getServletInfo();
        
          public void destroy();
        
        }
    

    浏览器地址栏输入一个地址,点击enter,发起请求

    1. client 发起一个请求request,首先client发起DNS解析先拿到服务器的ip:通过解析本地host文件,如没有,则将域名发到上级DNS,一直到能解析出ip。

    2. 发起网络通信,建立连接:三次握手

    3. 发送报文,到服务器然后服务器解析

    4. web服务器将请求转到servlet容器,web服务器将请求转到servlet容器

    5. servlet容器查找sertvlet实例,若没有,则实例化、servlet容器查找sertvlet实例,若没有,则实例化

    6. 响应请求,servlet实例调用service()方法处理请求响应请求,servlet实例调用service()方法处理请求
      如果是get 请求则走doGet()方法,如果是post请求走doPost()方法。
      (以Spring为例来说,会进入FrameworkServlet---->DispatcherServlet分发---->各种拦截器—>业务处理----> 返回客户端)

    7. 、终止,调用destroy()方法销毁 servlet。
      一般servlet容器被关闭后,servlet才会被销毁,也可以主动发动发起servlet销毁。

    展开全文
  • 浏览器一个HTTP请求过程

    千次阅读 2018-02-14 20:28:03
    浏览器一个请求过程 当我们在浏览器地址栏输入 www.xx.com ,然后回车, 这个请求背后经历了什么?以下是个人理解,如有偏差,请纠正! 首先重新温习下网络模型: 七层结构(至顶向下):应用层、表示层、...

    浏览器一个请求的过程

    当我们在浏览器地址栏输入 www.xx.com ,然后回车, 这个请求背后经历了什么?以下是个人理解,如有偏差,请纠正!


    首先重新温习下网络模型:
    • 七层结构(至顶向下):应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

    这里写图片描述

    client(浏览器) 与Server 通过HTTP协议通讯,HTTP协议属于”应用层协议”;HTTP基于TCP协议,TCP通过Socket进行通讯,TCP 被称为“传输层协议”。而HTTPS 还需要走会话层SSL\TLS等协议;传输层之下是网络层,这里主要是路由协议OSPF等进行路由选择,转发;再向下数据链路层主要是ARP、RARP协议完成IP和MAC地址互相解析,再向下是物理层主要是基于IEEE802.X等协议进行数据比特流转成高低电平的定义;

    当浏览器发出请求时候,首先通过应用层DNS协议得到对应IP地址,在这经历全球路由大接力;而传输层TCP协议采用“三次握手”协议建立连接和“四次握手”协议断开连接,然后数据链路层解析IP与MAC地址的映射;数据经过层层数据封包、解包过程,如下:
    这里写图片描述

    这里特别强调下三次握手建立连接,放图作为参考:
    这里写图片描述

    Wireshark中抓包图,如下:
    这里写图片描述

    四次握手断开连接:
    这里写图片描述

    客户端发起 FIN ACK, 服务端应答 ACK, 待服务端数据发送完成后,发送FIN ACK, 客户端应答 ACK, 连接断开。
    这里写图片描述

    注: WireShark中“四次握手“的数据并不一定会在一起。


    从应用服务角度分析

    数据交换主要通过HTTP 协议, HTTP协议是无状态协议。多数通过GET、POST方法,GET与POST区别:
    GET:请求获取指定的资源,不改变服务器状态,GET方法是幂等的,GET方法的报文主体没有任何语义。
    POST: 根据请求报文主体对指定的资源做出处理,改变服务器状态,POST方法不幂等。

    http方法不仅只有GET、POST, http1.1中含有如下方法:
    这里写图片描述

    RESTful API 就是利用这组方法来统一表述资源的状态转化。

    同时,由于http无状态协议的特点,服务端需要记录用户时,就需要用某种机制来识具体的用户, 通常采用cookies、session。

    session 本意是会话,是一个抽象概念。将 clinet 和 server 之间一对一的交互,抽象为“会话”,开发者为了实现一对一的“隔离”,中断和继续等操作,进而衍生出“会话状态”,也就是 session 的概念。

    如何维护这个session概念,很自然的想到在http请求报文中加入登录标识就可以了,这个登录标识就是cookie。而 cookie 是一个实际存在的东西,http 协议中定义在 header 中的字段。这种技术的实现就是利用了cookie技术。 cookie是存储key-value对的一个文件,务必记住,它是由服务器将cookie添加到response里一并返回给客户端,然后客户端会自动把response里的cookie接收下来,并且保存到本地,下次发出请求的时候,就会把cookie附加在request里,服务器在根据request里的cookie遍历搜索是否有与之符合的信息。

    cookie 虽然很方便,但是使用 cookie 有两个的弊端:

    1. cookie 中的所有数据在客户端就可以被修改。这就意味着数据非常容易被伪造,一些重要的数据就不能存放在 cookie 中;
    2. 如果 cookie 中数据字段太多会影响传输效率;

    为了解决这些问题,就产生了 session,
    session是一种服务器机制,使用Hash散列表结构来保存信息。
    1. 每个 session 都对应一个 session_id,通过 session_id 可以查询到对应的 session;
    2. session_id 通常是存放在客户端的 cookie 中,服务端存好 session 之后将对应的 session_id 设置在 cookie 中发送给客户端;
    3. 当请求到来时,服务端检查 cookie 中保存的 session_id 并通过这个 session_id 与服务器端的 session 关联起来,进行数据的保存和修改;

    这就是大多数人通常所说的session, 这里其实是指的 session实现。

    server 端通常采用Nginx、Apache;

    这里以PHP为例:
    Nginx接收到请求,进行一些验证,如黑白名单拦截等, 是否有限流限制,是否有负载均衡设置;
    如果请求的是静态内容,Nginx会将结果直接返回给用户。
    如果请求的是动态内容,Nginx会将请求交给fastcgi客户端, 通过fastcgi_pass将这个请求发送给进程php-fpm。Nginx 不支持对外部动态程序的直接调用或者解析 ,所有的外部程序必须通过FastCGI接口来调用。

    展开全文
  • 一个完整的HTTP请求过程详细

    万次阅读 多人点赞 2018-05-29 14:41:54
    一个完整的HTTP请求过程 整个流程 域名解析 —> 与服务器建立连接 —> 发起HTTP请求 —> 服务器响应HTTP请求,浏览器得到html代码 —> 浏览器解析html代码,并请求html代码中的资源...

    一个完整的HTTP请求过程

    整个流程

    域名解析 —> 与服务器建立连接 —> 发起HTTP请求 —> 服务器响应HTTP请求,浏览器得到html代码 —> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片) —> 浏览器对页面进行渲染呈现给用户

    1. 域名解析

    以Chrome浏览器为例:

    ① Chrome浏览器 会首先搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存),看自身的缓存中是否有https://www.cnblogs.com 对应的条目,而且没有过期,如果有且没有过期则解析到此结束。

    注:我们怎么查看Chrome自身的缓存?可以使用 chrome://net-internals/#dns 来进行查看

    ② 如果浏览器自身的缓存里面没有找到对应的条目,那么Chrome会搜索操作系统自身的DNS缓存,如果找到且没有过期则停止搜索解析到此结束.

    注:怎么查看操作系统自身的DNS缓存,以Windows系统为例,可以在命令行下使用 ipconfig /displaydns 来进行查看

    ③ 如果在Windows系统的DNS缓存也没有找到,那么尝试读取hosts文件(位于C:\Windows\System32\drivers\etc),看看这里面有没有该域名对应的IP地址,如果有则解析成功。

    ④ 如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向本地配置的首选DNS服务器(一般是电信运营商提供的,也可以使用像Google提供的DNS服务器)发起域名解析请求(通过的是UDP协议向DNS的53端口发起请求,这个请求是递归的请求,也就是运营商的DNS服务器必须得提供给我们该域名的IP地址),运营商的DNS服务器首先查找自身的缓存,找到对应的条目,且没有过期,则解析成功。如果没有找到对应的条目,则有运营商的DNS代我们的浏览器发起迭代DNS解析请求,它首先是会找根域的DNS的IP地址(这个DNS服务器都内置13台根域的DNS的IP地址),找打根域的DNS地址,就会向其发起请求(请问www.cnblogs.com这个域名的IP地址是多少啊?),根域发现这是一个顶级域com域的一个域名,于是就告诉运营商的DNS我不知道这个域名的IP地址,但是我知道com域的IP地址,你去找它去,于是运营商的DNS就得到了com域的IP地址,又向com域的IP地址发起了请求(请问www.cnblogs.com这个域名的IP地址是多少?),com域这台服务器告诉运营商的DNS我不知道www.cnblogs.com这个域名的IP地址,但是我知道cnblogs.com这个域的DNS地址,你去找它去,于是运营商的DNS又向cnblogs.com这个域名的DNS地址(这个一般就是由域名注册商提供的,像万网,新网等)发起请求(请问www.cnblogs.com这个域名的IP地址是多少?),这个时候cnblogs.com域的DNS服务器一查,诶,果真在我这里,于是就把找到的结果发送给运营商的DNS服务器,这个时候运营商的DNS服务器就拿到了www.cnblogs.com这个域名对应的IP地址,并返回给Windows系统内核,内核又把结果返回给浏览器,终于浏览器拿到了www.cnblogs.com 对应的IP地址,该进行一步的动作了。

    注:一般情况下是不会进行以下步骤的

    如果经过以上的4个步骤,还没有解析成功,那么会进行如下步骤(以下是针对Windows操作系统):

    ⑤ 操作系统就会查找NetBIOS name Cache(NetBIOS名称缓存,就存在客户端电脑中的),那这个缓存有什么东西呢?凡是最近一段时间内和我成功通讯的计算机的计算机名和Ip地址,就都会存在这个缓存里面。什么情况下该步能解析成功呢?就是该名称正好是几分钟前和我成功通信过,那么这一步就可以成功解析。

    ⑥ 如果第⑤步也没有成功,那会查询WINS 服务器(是NETBIOS名称和IP地址对应的服务器)

    ⑦ 如果第⑥步也没有查询成功,那么客户端就要进行广播查找

    ⑧ 如果第⑦步也没有成功,那么客户端就读取LMHOSTS文件(和HOSTS文件同一个目录下,写法也一样)

    如果第八步还没有解析成功,那么就宣告这次解析失败,那就无法跟目标计算机进行通信。只要这八步中有一步可以解析成功,那就可以成功和目标计算机进行通信。

    2. 与服务器建立连接

    2.1 TCP连接的建立

    客户端的请求到达服务器,首先就是建立TCP连接

    来源于:https://blog.csdn.net/u012248450/article/details/51036329

    1. Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号(seq = 0 就代表这是第0号包),这时候Client进入syn_sent状态,表示客户端等待服务器的回复

    2. Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1,同时表明x为止的所有数据都已正确收到(ack=1其实是ack=0+1,也就是期望客户端的第1个包),seq = y 表示Server 自己的初始序号(seq=0就代表这是服务器这边发出的第0号包)。这时服务器进入syn_rcvd,表示服务器已经收到Client的连接请求,等待client的确认。

    3. Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(代表期望收到服务器的第1个包),Client自己的序号seq= x + 1(表示这就是我的第1个包,相对于第0个包来说的),一旦收到Client的确认之后,这个TCP连接就进入Established状态,就可以发起http请求了。

    问题1:TCP 为什么需要3次握手?

    2个计算机通信是靠协议(目前流行的TCP/IP协议)来实现,如果2个计算机使用的协议不一样,那是不能进行通信的,所以这个3次握手就相当于试探一下对方是否遵循TCP/IP协议,协商完成后就可以进行通信了,当然这样理解不是那么准确。

    问题2:为什么HTTP协议要基于TCP来实现?

    目前在Internet中所有的传输都是通过TCP/IP进行的,HTTP协议作为TCP/IP模型中应用层的协议也不例外,TCP是一个端到端的可靠的面向连接的协议,所以HTTP基于传输层TCP协议不用担心数据的传输的各种问题。

    2.2 常见TCP连接限制

    2.2.1 修改用户进程可打开文件数限制

    在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的文件数限制,windows上是256,linux是1024,这个博客的服务器是65535

    2.2.2 修改网络内核对TCP连接的有关限制

    在Linux上编写支持高并发TCP连接的客户端通讯处理程序时,有时会发现尽管已经解除了系统对用户同时打开文件数的限制,但仍会出现并发TCP连接数增加到一定数量时,再也无法成功建立新的TCP连接的现象。出现这种现在的原因有多种。
    第一种原因可能是因为Linux网络内核对本地端口号范围有限制。此时,进一步分析为什么无法建立TCP连接,会发现问题出在connect()调用返回失败,查看系统错误提示消息是“Can’t assign requestedaddress”。同时,如果在此时用tcpdump工具监视网络,会发现根本没有TCP连接时客户端发SYN包的网络流量。这些情况说明问题在于本地Linux系统内核中有限制。

    其实,问题的根本原因在于Linux内核的TCP/IP协议实现模块对系统中所有的客户端TCP连接对应的本地端口号的范围进行了限制(例如,内核限制本地端口号的范围为1024~32768之间)。当系统中某一时刻同时存在太多的TCP客户端连接时,由于每个TCP客户端连接都要占用一个唯一的本地端口号(此端口号在系统的本地端口号范围限制中),如果现有的TCP客户端连接已将所有的本地端口号占满,则此时就无法为新的TCP客户端连接分配一个本地端口号了,因此系统会在这种情况下在connect()调用中返回失败,并将错误提示消息设为“Can’t assignrequested address”。

    2.3 TCP四次挥手

    当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。

    来源于:https://blog.csdn.net/LRH0211/article/details/72724361

    第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

    第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;

    第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;

    第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

    问题1:为什么要四次分手?

    TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。

    3. 发起HTTP请求

    3.1 HTTP协议

    HTTP是一个客户端和服务器端请求和应答的标准(TCP)。客户端是终端用户,服务器端是网站。通过使用Web浏览器、网络爬虫或者其它的工具,客户端发起一个到服务器上指定端口(默认端口为80)的HTTP请求。

    通俗来讲,他就是计算机通过网络进行通信的规则,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据。目前任何终端(手机,笔记本电脑。。)之间进行任何一种通信都必须按照Http协议进行,否则无法连接。

    3.1.1 四个基于
    1. 请求与响应:客户端发送请求,服务器端响应数据
    2. 无状态的:协议对于事务处理没有记忆能力,客户端第一次与服务器建立连接发送请求时需要进行一系列的安全认证匹配等,因此增加页面等待时间,当客户端向服务器端发送请求,服务器端响应完毕后,两者断开连接,也不保存连接状态,一刀两断!恩断义绝!从此路人!下一次客户端向同样的服务器发送请求时,由于他们之前已经遗忘了彼此,所以需要重新建立连接。
    3. 应用层: Http是属于应用层的协议,配合TCP/IP使用。
    4. TCP/IP: Http使用TCP作为它的支撑运输协议。HTTP客户机发起一个与服务器的TCP连接,一旦连接建立,浏览器(客户机)和服务器进程就可以通过套接字接口访问TCP。

    3.2 HTTP请求报文

    一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。

    来源于:https://blog.csdn.net/LRH0211/article/details/72724361

    3.2.1 请求行

    请求行分为三个部分:请求方法、请求地址和协议版本

    请求方法

    HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE。

    最常的两种GET和POST,如果是RESTful接口的话一般会用到GET、POST、DELETE、PUT。

    请求地址

    URL:统一资源定位符,是一种自愿位置的抽象唯一识别方法。

    组成:<协议>://<主机>:<端口>/<路径> 注:端口和路径有时可以省略(HTTP默认端口号是80)

    https://localhost:8080/index.html?key1=value1&keys2=value2

    协议版本

    协议版本的格式为:HTTP/主版本号.次版本号,常用的有HTTP/1.0和HTTP/1.1

    3.2.2 请求头部

    请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。

    常见请求头如下:

    来源于:https://blog.csdn.net/LRH0211/article/details/72724361

    请求头部的最后会有一个空行,表示请求头部结束,接下来为请求数据,这一行非常重要,必不可少。

    3.2.3 请求数据

    可选部分,比如GET请求就没有请求数据。

    下面是一个POST方法的请求报文:

    POST  /index.php HTTP/1.1    请求行 
    Host: localhost 
    User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2  请求头 
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 
    Accept-Language: zh-cn,zh;q=0.5 
    Accept-Encoding: gzip, deflate 
    Connection: keep-alive 
    Referer: http://localhost/ 
    Content-Length:25 
    Content-Type:application/x-www-form-urlencoded 
      空行 
    username=aa&password=1234  请求数据
    

    4. 服务器响应HTTP请求,浏览器得到html代码

    4.1 负载均衡

    接收到HTTP请求之后,就轮到负载均衡登场了,它位于网站的最前端,把短时间内较高的访问量分摊到不同机器上处理。负载均衡方案有软件、硬件两种

    4.1.1 负载均衡硬件方案

    F5 BIG-IP是著名的硬件方案,但这里不作讨论

    4.1.2 负载均衡软件方案

    有LVS HAProxy Nginx等,留作以后补充

    在典型的Rails应用部署方案中,Nginx的作用有两个

    1. 处理静态文件请求
    2. 转发请求给后端的Rails应用
      这是一个简单的Nginx配置文件

    来源于:https://blog.csdn.net/u012248450/article/details/51036329

    后端的Rails服务器通过unix socket与Nginx通信,Nginx伺服public文件夹里的静态文件给用户

    待完善…

    4.2 Rails(应用服务器)

    4.3 数据库(数据库服务器)

    4.4 Redis、Memercache(缓存服务器)

    4.5 消息队列

    4.6 搜索

    4.7 HTTP响应报文

    来源于:https://blog.csdn.net/LRH0211/article/details/72724361

    HTTP响应报文主要由状态行、响应头部、空行以及响应数据组成。

    4.7.1 状态行

    由3部分组成,分别为:协议版本,状态码,状态码描述。

    其中协议版本与请求报文一致,状态码描述是对状态码的简单描述,所以这里就只介绍状态码。

    状态码

    状态代码为3位数字。

    • 1xx:指示信息–表示请求已接收,继续处理。
    • 2xx:成功–表示请求已被成功接收、理解、接受。
    • 3xx:重定向–要完成请求必须进行更进一步的操作。
    • 4xx:客户端错误–请求有语法错误或请求无法实现。
    • 5xx:服务器端错误–服务器未能实现合法的请求。

    下面列举几个常见的:

    来源于:https://blog.csdn.net/LRH0211/article/details/72724361

    4.7.2 响应头部

    与请求头部类似,为响应报文添加了一些附加信息

    常见响应头部如下:

    来源于:https://blog.csdn.net/LRH0211/article/details/72724361

    4.7.3 响应数据

    用于存放需要返回给客户端的数据信息。

    下面是一个响应报文的实例:

    HTTP/1.1 200 OK  状态行 
    Date: Sun, 17 Mar 2013 08:12:54 GMT  响应头部 
    Server: Apache/2.2.8 (Win32) PHP/5.2.5 
    X-Powered-By: PHP/5.2.5 
    Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/ 
    Expires: Thu, 19 Nov 1981 08:52:00 GMT 
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 
    Pragma: no-cache 
    Content-Length: 4393 
    Keep-Alive: timeout=5, max=100 
    Connection: Keep-Alive 
    Content-Type: text/html; charset=utf-8 
      空行 
      响应数据 
    
    HTTP响应示例 
    
    
    Hello HTTP! 
    

    关于请求头部和响应头部的知识点很多,这里只是简单介绍。

    5. 浏览器解析html代码,并请求html代码中的资源

    浏览器拿到index.html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这个时候就用上keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里的顺序,但是由于每个资源大小不一样,而浏览器又多线程请求请求资源,所以从下图看出,这里显示的顺序并不一定是代码里面的顺序。

    浏览器在请求静态资源时(在未过期的情况下),向服务器端发起一个http请求(询问自从上一次修改时间到现在有没有对资源进行修改),如果服务器端返回304状态码(告诉浏览器服务器端没有修改),那么浏览器会直接读取本地的该资源的缓存文件。

    图

    详细的浏览器工作原理请看:http://kb.cnblogs.com/page/129756/

    6. 浏览器对页面进行渲染呈现给用户

    最后,浏览器利用自己内部的工作机制,把请求到的静态资源和html代码进行渲染,渲染之后呈现给用户。

    参考文献

    一次完整的HTTP请求与响应涉及了哪些知识? https://blog.csdn.net/LRH0211/article/details/72724361

    一次完整的HTTP请求过程 https://www.cnblogs.com/engeng/articles/5959335.html

    后端知识体系–一次完整的HTTP请求 https://blog.csdn.net/u012248450/article/details/51036329

    展开全文
  • tomcat处理一个请求过程

    千次阅读 2017-07-16 22:32:50
    首先,tomcat是一个基于组件的服务器,它的构成组件都是可配置的,可以在tomcat目录下conf/server.xml中进行配置。其配置文件结构如下:顶层类元素:一个配置文件中只能有一个元素,可包含多个Service。 顶层类元素...

    首先,tomcat是一个基于组件的服务器,它的构成组件都是可配置的,可以在tomcat目录下conf/server.xml中进行配置。其配置文件结构如下:

    <Server>顶层类元素:一个配置文件中只能有一个<Server>元素,可包含多个Service。
        <Service>顶层类元素:本身不是容器,可包含一个Engine,多个Connector。
            <Connector/>连接器类元素:代表通信接口。
            <Engine>容器类元素:为特定的Service组件处理所有客户请求,可包含多个Host。engine为顶层Container
               <Host>容器类元素:为特定的虚拟主机处理所有客户请求,可包含多个Context。
                  <Context>容器类元素:为特定的Web应用处理所有客户请求。代表一个应用,包含多个Wrapper(封装了servlet)
                  </Context>
               </Host>
            </Engine>
         </Service>
    </Server>

    tomcat是基于组件的,分层避不可免,对于一个请求的处理,tomcat的链式调用比较长,让我们从接收请求开始说起。

    tomcat处理socket请求的IO模型有BIO、NIO、AIO等,后两者IO模型比较复杂,而本文的主要关注点不在这,因此将tomcat配置成BIO模式。

    tomcat接收请求的代码在Acceptor类中:

    /**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     * JIoEndpoint 的内部类
     */
    protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            // Loop until we receive a shutdown command
            while (running) {
                //if we have reached max connections, wait
                countUpOrAwaitConnection();
    
                Socket socket = null;
                //阻塞住 监听新的socket请求
                socket = serverSocketFactory.acceptSocket(serverSocket);
    
                // Configure the socket
                if (running && !paused && setSocketOptions(socket)) {
                    // Hand this socket off to an appropriate processor 首先使用processSocket()简单处理socket
                    if (!processSocket(socket)) {
                        countDownConnection();
                        // Close socket right away
                        closeSocket(socket);
                    }
                } else {
                    countDownConnection();
                    // Close socket right away
                    closeSocket(socket);
                }
    
            }
        }
    }

    Acceptor 继承了 AbstractEndpoint.Acceptor ,间接实现了Runnable接口,tomcat在运行时将Acceptor运行在一个后台线程内,单独监听socket请求,此线程的调用栈如下:

    这里写图片描述

    processSocket()方法如下:

    /**
     * Process a new connection from a new client. Wraps the socket so
     * keep-alive and other attributes can be tracked and then passes the socket
     * to the executor for processing.
     */
    protected boolean processSocket(Socket socket) {
        // Process the request from this socket
        SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
        wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
        wrapper.setSecure(isSSLEnabled());
        // During shutdown, executor may be null - avoid NPE
        if (!running) {
            return false;
        }
        getExecutor().execute(new SocketProcessor(wrapper));
        return true;
    }

    processSocket方法主要工作为将socket请求信息进行封装,然后将一个实现了Runnable接口的并包含socket信息的SocketProcessor对象交给线程池,进行执行,然后Acceptor线程从该方法返回,重新监听端口上的socket请求。

    线程池receive到该worker后,取出一个线程处理socket请求,其调用栈如下:

    这里写图片描述

    调用栈底部的processor和handler主要处理TCP和HTTP协议的一些细节,CoyoteAdapter实现了对request输入流的编解码工作,并从service中获取顶层Container,将request和response交与容器组件,关键代码如下:

    connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

    其中,在每一层容器中对请求的处理都运用了责任链模式,即所谓的Pipeline-Value处理模式,pipeline就像一个管道,表示对请求的链式处理过程,其中包含多个value,每个value代表对请求的一次处理,处理完成之后交给next Value进行处理,每层容器都至少有一个Value,这个Value被成为BaseValue,在pipeline中位于最后一个位置,比如Engine容器的BaseValue为StandardEngineValue。每一层的BaseValue会调用下一层容器的pipeline的第一个Value,由此形成一个长链:

    这里写图片描述

    到达pipeline长链最后一个value StandardWrapperValue后,会触发另外一个责任链模式:filterChain责任链,也就是我们平常熟悉的在web项目中配置的filter被调用的位置。

    StandardWrapperValue调用filterChain代码如下:

    //StandardWrapperValue类
    
    public final void invoke(Request request, Response response){
        ...
        // Create the filter chain for this request
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);
        ...
        //开始调用filterChain
        filterChain.doFilter(request.getRequest(), response.getResponse());
        ...
    }

    ApplicationFilterFactory.getInstance()方法如下:

    /**
     * Return the factory instance.
     */
    //ApplicationFilterFactory类
    public static ApplicationFilterFactory getInstance() {
        if (factory == null) {
            factory = new ApplicationFilterFactory();
        }
        return factory;
    }

    看到这个单例方法我有点蒙,这不明摆着存在竞态条件么,完全线程不安全的单例工厂 - - ,源码为tomcat7.0,难道这样没问题?

    createFilterChain创建filterChain的代码如下:

    //ApplicationFilterChain类
    
    public ApplicationFilterChain createFilterChain
            (ServletRequest request, Wrapper wrapper, Servlet servlet) {
         ApplicationFilterChain filterChain = null;
         ...
         filterChain = new ApplicationFilterChain();
         ...
         filterChain.setServlet(servlet);
         ...
         // Acquire the filter mappings for this Context
         StandardContext context = (StandardContext) wrapper.getParent();
         FilterMap filterMaps[] = context.findFilterMaps();
    
         // If there are no filter mappings, we are done
         if ((filterMaps == null) || (filterMaps.length == 0))
             return (filterChain);
    
         // Acquire the information we will need to match filter mappings
        String servletName = wrapper.getName();
    
        // Add the relevant path-mapped filters to this filter chain 根据请求url找到对应的filter指针,添加进来
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            ...
            filterChain.addFilter(filterConfig);
        }
    
        // Add filters that match on servlet name second
        //如果配置了根据servlet名称过滤,则再寻找一遍filter
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            ...
            //addFilter时会对filter指针排重
            filterChain.addFilter(filterConfig);
        }
    
        // Return the completed filter chain
        return (filterChain);
    }

    创建filterChain代码比较简单,首先创建一个FilterChain对象,然后把url对应的servlet放进去,相当于pipeline-value模式中的最后一个BaseValue。之后从ServletContext中拿出所有的filter,然后根据url和servlet name找到符合条件的filter,根据顺序组装到filterChain中。

    可能看到这里有些人会想,为什么要为每次请求都创建一个新的FilterChain对象呢,这显得有些不合常理,因为对于tomcat的大部分组件、filter、servlet都是单例的,而且频繁new操作有些消耗资源吧。确实是这样的,但这里将FilterChain做成prototype是有原因的,因为对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filter的position,以便继续向下调用filter。这里的filter不能像pipeline-value模式那样组装起来,而是依靠filterChain来决定每个url的调用顺序。

    创建完filterchain后,StandardWrapperValue就开始调用filterChain的doFilter方法了:

    //ApplicationFilterChain类
    public void doFilter(ServletRequest request, ServletResponse response){
    ...
        internalDoFilter(request,response);
    ...
    }
    
    private void internalDoFilter(ServletRequest request, 
                                      ServletResponse response){
        ...
        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            filter = filterConfig.getFilter();
            filter.doFilter(request, response, this);
            return;
        }
    
        ...
    
        // We fell off the end of the chain -- call the servlet instance
        servlet.service(request, response);
        ...
    }

    pos为filterchain对象内的一个变量,用来标记当前调用的filter的位置,filter.doFilter(request, response, this),这句代码即调用咱们的filter了,记不记得咱们自己写filter时,如果不直接close outputstream,都会在doFilter最后写一句:filterChain.doFilter(req,res)来回调filterChain,让它继续调用接下来的filter。

    在pos == n (filter数组长度)时,也就是filter调用完了,servlet.service(request, response)这一句便是调用我们的servlet了。我们的servlet在处理完请求后,再一步一步按原路返回,将处理结果通过socket写回客户端。

    至此,一个请求就被tomcat处理完了。

    展开全文
  • TomcatServer处理一个http请求过程 假设来自客户的请求为: http://localhost:8080/wsota/wsota_index.jsp 1)请求被发送到本机端口8080,被在那里侦听的CoyoteHTTP/1.1Connector获得 2)Connector把该请求交给它...
  • 相信看完这张图后大家对请求也有了大致的了解了,当前其中还有很多细节小编没有画出来,然后我们来看看部分过程的细节 所以其流程大致如下: ①:DNS解析域名得到IP地址 ②:客户端与服务器建立连接(TCP三次...
  • 下面我将从以下几个方面阐述一个 WEB请求过程到底是怎样: - 浏览器缓存 - DNS域名解析 - TCP连接 - HTTP请求与响应 浏览器的缓存机制 这里将浏览器机制放在第一步是考虑如果浏览器中有了缓存数据,浏览器...
  • 次完整的HTTP请求过程

    万次阅读 多人点赞 2017-10-10 16:06:50
    以上完整表示了HTTP请求和响应的7步骤,下面从TCP/IP协议模型的角度来理解HTTP请求和响应如何传递的。 二、TCP/IP协议 TCP/IP协议模型(Transmission Control Protocol/Internet Protocol),包含了系列构成...
  • 理解一个简单的网页请求过程

    千次阅读 2014-10-29 20:14:05
    我们似乎每天都要做这样一件事情,...下面我们就从三个方面理解这个过程一个是浏览器,二个是服务器,第三个是浏览器和服务器之间通信的协议。在理解这三方面之前我们必须先搞明白将这三方面联系起来的一个词:web。
  • Springboot中一个请求过来的执行过程

    万次阅读 2018-07-18 16:24:34
    一个请求进来的时候,就是通过ReactorHttpHandlerAdapter的apply()方法然后进入了了HttpWebHandlerAdapter类的handle方法中执行了DispatcherHandler的handle方法: return Flux.fromIterable(this....
  • 缓存中无法找到ip,浏览器会进行一个系统调用,查询hosts文件。如果找到,直接返回ip,否则下一步。进行1 和2 本地查询无果,只能借助于网络,路由器一般都会有自己的DNS缓存,ISP服务商DNS缓存,这时一般都能够得到...
  • Tomcat结构以及处理一个请求过程

    千次阅读 2016-03-18 10:30:32
    Tomcat是一个基于组件的服务器,它的构成组件都是可配置的,其中最外层的组件是Catalina Servlet容器,其他的组件按照一定的格式要求配置在这个顶层容器中。Tomcat的各个组件是在\conf\server.xml文件中配置的,...
  • SpringMVC是一个基于DispatcherServlet的MVC框架,每一个请求最先访问的都是DispatcherServlet,DispatcherServlet负责转发每一个Request请求给相应的Handler,Handler处理以后再返回相应的视图(View)和模型(Model)...
  • 一个http请求发送到后端的详细过程

    千次阅读 2017-03-30 19:34:23
    首先http是一个应用层的协议,在这个层的协议,只是一种通讯规范,也就是因为双方要...1.连接 当我们输入这样一个请求时,首先要建立一个socket连接,因为socket是通过ip和端口建立的,所以之前还有一个DNS解析过程
  • HTTP请求的完全过程

    万次阅读 多人点赞 2019-05-27 11:22:45
    HTTP请求的完全过程 1.1 浏览器根据域名解析IP地址 浏览器根据访问的域名找到其IP地址。DNS查找过程如下: 浏览器缓存:首先搜索浏览器自身的DNS缓存(缓存的时间比较短,大概只有1分钟,且只能容纳1000条缓存)...
  • 次php请求过程

    千次阅读 2019-06-20 10:29:25
    1. 搭建环境: (1) 本机虚拟机地址192.168.100.142。... (2) nginx容器配置文件中通过proxy_pass将php请求转发到php-fpm...(3) 为实验项目配置域名my.test.com,项目目录设定为/{rootPath}/yii/frontend/web(这是...
  • ajax请求过程

    千次阅读 2019-07-05 15:32:48
    (1)创建ajax对象 var xhr = new XMLHttpRequest(); (2)打开请求 xhr.open('GET', url, true); (3)发送请求 xhr.send(); 发送请求到服务器 ...(4.1)当readystate值从一个值变为另一个值时,都会触发rea...
  • Tomcat处理一个HTTP请求过程 假设来自客户的请求为: http://localhost:8080/yy/index.jsp 1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得 2) Connector把该请求交给它所在的...
  • 次完整的http请求过程

    千次阅读 2014-07-23 09:32:00
    一个超时就是客户端等待请求消息返回信息的最长时间。 HTTP协议的请求和响应消息如果没有发送并传递成功的话,不保存任何已传递的信息。比如,单击“提交”按牛,如果表单没有发出去,则浏览器
  • spring mvc请求过程

    千次阅读 2014-01-17 13:26:20
    Spring mvc处理请求过程 1、 首先客户端发送一个HTTP请求,Web服务器接收这个请求,如果匹配DispatcherServlet的请求映射路径,web容器将请求转交给DispatcherServlet处理。 2、 DispatcherServlet接收到请求,...
  • HTTP的请求过程

    万次阅读 多人点赞 2018-01-05 15:37:25
    、简单描述次Http的请求过程 域名解析 –> 发起TCP的3次握手 –> 建立TCP连接后发起http请求 –> 服务器响应http请求,浏览器得到html代码 –> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等...
  • Tomcat处理HTTP请求过程分析

    千次阅读 2018-12-14 14:43:02
    Tomcat处理HTTP请求过程分析 一、Tomcat是什么? Tomcat是一个web应用服务器,是一个Servlet/Jsp容器,主要负责将客户端请求传递给对应的Servlet,并且将Servlet的响应数据返回给客户端。 Tomcat是基于组件的...
  • 次完整的http请求处理过程

    千次阅读 2018-01-25 21:57:28
    2、接收请求:接收客户端请求报文中对某资源的请求过程 3、处理请求:服务器对请求报文进行解析,并获取请求的资源及请求方法等相关信息,根据方法,资源,首部和可选的主体部分对请求进行处理 元数据:请求...
  • 一个网页从开始请求到最终显示的完整过程一个网页从请求到最终显示的完整过程一般可分为如下7个步骤: 1. 在浏览器中输入网址; 2. 发送至DNS服务器并获得域名对应的WEB服务器的IP地址; 3. 与WEB服务器建立TCP...
  • 域名解析-&amp;amp;gt;域名 -&amp;amp;gt;缓存-&amp;amp;gt;根域dns-&amp;amp;gt;顶级域dns-&amp;amp;gt;...4.浏览器发起一个DNS的系统调用 域名解析 5.客户端通过随机端口使

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,558,513
精华内容 623,405
关键字:

一个请求的过程