Tomcat 浅度解析

一、Tomcat 顶层架构

Tomcat 中最顶层的容器是 Server,代表着整个服务器,负责管理和启动各个 Service,同时监听 8005 端口发过来的 shutdown 命令,用于关闭整个容器。

从上图中可以看出,一个 Server 可以包含至少一个 Service,用于具体提供服务。

Service 主要包含两个部分:Connector 和 Container。从上图中可以看出 Tomcat 的心脏就是这两个组件,他们的作用如下:

1、Connector 提供 Socket 用于接受请求并将请求封装成 Request 和 Response 来具体处理;

2、Container 用于封装和管理 Servlet,以及具体处理 Request 请求;

小结:一个 Tomcat 中只有一个 Server,一个 Server 可以包含多个 Service,一个 Service 只有一个 Container,但是可以有多个 Connector。这是因为一个服务可以有多个连接,如同时提供括 http 和 https 连接,也可以提供相同协议不同端口的连接。

示意图如下:

Service 其他组件说明:

Loader:封装了 Java ClassLoader,用于 Container 加载类文件;

Realm:Tomcat 中为 Web 应用程序提供访问认证和角色管理的机制;

JMX:Java SE 中定义技术规范,是一个为应用程序、设备、系统等植入管理功能的框架,通过 JMX 可以远程监控 Tomcat 的运行状态;

Jasper:Tomcat 的 Jsp 解析引擎,用于将 Jsp 转换成 Java 文件,并编译成 Class 文件。

Session:负责创建和管理 Session,以及 Session 的持久化(可自定义),支持 Session 的集群。

Pipeline:在容器中充当管道的作用,管道中可以设置各种 Valve (阀门),请求和响应在经由管道中各个阀门处理,提供了一种灵活可配置的处理请求和响应的机制。

Naming:命名服务,JNDI,Java 命名和目录接口,是一组在 Java 应用中访问命名和目录服务的 API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象,目录服务也是一种命名服务,对象不但有名称,还有属性。Tomcat 中可以使用 JNDI 定义数据源、配置信息,用于开发与部署的分离。

二、Connector 和 Container 的微妙关系

一个请求发送到 Tomcat 之后,首先经过 Service 然后会交给我们的 Connector,Connector 用于接收请求并将接收的请求封装为 Request 和 Response 来具体处理,Request 和 Response 封装完之后再交由 Container 进行处理,Container 处理完请求之后再返回给 Connector,最后再由 Connector 通过 Socket 将处理的结果返回给客户端,这样整个请求的就处理完了!

三、Connector 架构分析

Connector 用于接受请求并将请求封装成 Request 和 Response,然后交给 Container 进行处理,Container 处理完之后再交给 Connector 返回给客户端。

Connector 是使用 ProtocolHandler 来处理请求的,不同的 ProtocolHandler 代表不同的连接类型。比如:Http11Protocol 使用的是普通 Socket 来连接的,Http11NioProtocol 使用的是 NioSocket 来连接的。

其中ProtocolHandler包含了三个部件:Endpoint、Processor、Adapter。

1、Endpoint 用来处理底层 Socket 的网络连接;

2、Processor 用于将 Endpoint 接收到的 Socket 封装成 Request;

3、Adapter 用于将 Request 交给 Container 进行具体的处理。

Endpoint 的抽象实现 AbstractEndpoint 里面定义了 Acceptor 和 AsyncTimeout 两个内部类和一个 Handler 接口。

1、Acceptor 用于监听请求;

2、AsyncTimeout 用于检查异步 Request 的超时;

3、Handler 用于处理接收到的 Socket,在内部调用 Processor 进行处理。

四、Container 架构分析

Container 用于封装和管理 Servlet,以及具体处理 Request 请求,在 Connector 内部包含了4个子容器。

4个子容器的作用分别是:

1、Engine:引擎,用来管理多个站点,一个 Service 最多只能有一个 Engine,负责和处理连接器的请求与响应;

2、Host:代表一个站点,也可以叫虚拟主机,通过配置 Host 就可以添加站点;

3、Context:代表一个应用程序,对应着平时开发的一套程序,或者一个 WEB-INF目录以及下面的web.xml 文件,提供 Wrapper 运行的环境;

4、Wrapper:每一 Wrapper 封装着一个 Servlet,Wrapper 容器对应 Servlet 的处理过程。它开启 Servlet 的生命周期,根据 Context 给出的信息以及解析 web.xml 中的映射关系,负责装载相关的类,初始化 Servlet 对象 init()、执行 Servlet 代码 service() 以及服务结束时 Servlet 对象的销毁 destory()。

Context 和 Host 的区别是 Context 表示一个应用,我们的 Tomcat 中默认的配置下 webapps 下的每一个文件夹目录都是一个 Context,其中 ROOT 目录中存放着主应用,其他目录存放着子应用,而整个 webapps 就是一个 Host 站点。

五、Container 处理请求

Container 处理请求是使用 Pipeline-Valve 管道来处理的!(Valve是阀门之意)

Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将处理后的请求返回,再让下一个处理者继续处理。

Pipeline 可以理解为现实中的管道,Valve 为管道中的阀门,Request 和 Response 对象在管道中经过各个阀门的处理和控制。

Valve 中主要的三个方法:setNext、getNext、invoke;Valve 之间的关系是单向链式结构,本身 invoke 方法中会调用下一个 Valve 的 invoke 方法。

每个 Pipeline 都有特定的 Valve,而且是在管道的最后一个执行,这个 Valve 叫做 BaseValve,BaseValve 是不可删除的;并且,在上层容器的管道的 BaseValve 中会调用下层容器的管道。

我们知道 Container 包含四个子容器,而这四个子容器对应的 BaseValve 分别在:StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValue。

Pipeline 的处理流程图如下:

Container在接收到请求后,首先调用最顶层容器的Pipeline来处理,这里的最顶层容器的Pipeline就是EnginePipeline(Engine的管道);

在 Engine 的管道中依次会执行 EngineValve1、EngineValve2 等等,最后会执行 StandardEngineValve,在StandardEngineValve 中会调用 Host 管道,然后再依次执行 Host 的 HostValve1、HostValve2 等,最后在执行 StandardHostValve,然后再依次调用 Context 的管道和 Wrapper 的管道,最后执行到 StandardWrapperValve。

当执行到 StandardWrapperValve 的时候,会在 StandardWrapperValve 中创建 FilterChain,并调用其 doFilter 方法来处理请求。这个 FilterChain 包含着我们配置的与请求相匹配的 Filter 和 Servlet,其 doFilter 方法会依次调用所有的 Filter 的 doFilter 方法和 Servlet 的 service 方法,这样请求就得到了处理!

当所有的 Pipeline-Valve 都执行完之后,并且处理完了具体的请求,这个时候就可以将返回的结果交给 Connector 了,Connector 在通过 Socket 的方式将结果返回给客户端。

六、如何确定请求由谁处理?

当一个请求被发送到 Tomcat 所在的主机时,如何确定最终哪个 Web 应用来处理该请求呢?

1、根据协议和端口号选定 Service 和 Engine

Service 中的 Connector 组件可以接收特定端口的请求,因此,当 Tomcat 启动时,Service 组件就会监听特定的端口。

在 Tomcat 配置文件中,Catalina 这个 Service 监听了 8080 端口(基于 HTTP 协议)和 8009 端口(基于 AJP 协议)。当请求进来时,Tomcat 便可以根据协议和端口号选定处理请求的 Service。而 Service 一旦选定,Engine 也就确定了。

通过在 Server 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

2、根据域名或 IP 地址选定 Host

Service 确定后,Tomcat 在 Service 中寻找名称与域名/IP 地址匹配的 Host 处理该请求。如果没有找到,则使用 Engine 中指定的 defaultHost 来处理该请求。

在 Tomcat 配置文件中,由于只有一个 Host(name 属性为 localhost),因此该 Service/Engine 的所有请求都交给该 Host 处理。

3、根据 URI 选定 Context/Web 应用

Host 确定后,Tomcat 会根据 Web 应用的 path 属性与 URI 的匹配程度来选择 Web 应用处理相应请求。如果一个 Context 元素的 path 属性为 "",那么这个 Context 是虚拟主机的默认 Web 应用。当请求的 URI 与所有的 path 都不匹配时,使用该默认 Web 应用来处理。

4、举个例子

一个客户端请求:http://localhost:8080/kdzs/aaa/bbb.jsp

1)用户点击浏览器网页内容,请求被发送到本机端口 8080,被在那里监听的 HTTP/1.1 Connector 获得。

2)Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待 Engine 的回应。

3)Engine 获得请求 localhost/kdzs/aaa/bbb.jsp,匹配所有的虚拟主机 Host。

4)Engine 匹配到 name 为 "localhost" 的 Host(即使匹配不到也把请求交给该 Host 处理,因为该 Host 被定义为该 Engine 的默认主机)。然后该 Host 获得请求 kdzs/aaa/bbb.jsp ,并匹配它所拥有的所有的 Context。

5)Host 匹配到 path 为 "kdzs" 的 Context(如果匹配不到就把该请求交给 path 为 "" 的 Context 去处理)。然后该 Context 获得请求 aaa/bbb.jsp,在它的 mapping table 中寻找出对应的 Servlet。Context 匹配到 URL PATTERN 为 *.jsp 的 Servlet,对应于 JspServlet 类。

6)构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet 的 doGet() 或 doPost(),执行业务逻辑、数据存储等程序。

7)Context 把执行完之后的 HttpServletResponse 对象返回给 Host。

8)Host 把 HttpServletResponse 对象返回给 Engine。

9)Engine 把 HttpServletResponse 对象返回 Connector。

10)Connector 把 HttpServletResponse 对象转换为字节流并返回给客户端 Browser。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值