大飞带你深入理解Tomcat(七)

经过前几篇的tomcat分解,大体能理解tomcat操作原理,那么真实的tomcat又是一个怎么的架构呢?本篇我们站在上帝角度来看下tomcat整个系统结构。
tomcat组件结构

下面是上图各组件的介绍:

Server

最顶级组件,代码着tomcat实例,在JVM有且仅有一份。引入事件处理机制,方便Server在生命周期不同阶段处理不同逻辑。引入GlobalNamingResources组件,为JNDI提供支持。

Service

tomcat功能服务的抽象,一个server组件中至少有一个service, 是请求从发起到响应所有参与组件集合体。分为2个核心部分:Connector组件和Container(Engine/Host/Context/Wrapper统称)组件。注意,一个Service包含若干个Connector组件,用于处理基于不同通讯协议的连接相关事情, 比如HTTP协议、AJP协议等连接。而一个Service只能拥有一个Engine组件。

Connector

负责接收客户端连接并接收消息报文,简单讲就是将socket转换成Reqeust跟Response对象,然后交给Engine容器处理。Connector组件包含的模块
》协议模块(Protocol)
识别客户端请求是基于什么协议,目前tomcat支持HTTP协议、HTTPS协议,HTTP2协议和AJP协议,对应的处理类Http11AprProtocol类,Http2Protocol类,AjpAprProtocol类
》Acceptor组件
用于监听连接端口,当请求到来时,直接丢入Executor线程池统一处理。
》Processor组件
处理请求(分析协议,获取请求数据,封装请求对象等),将处理结果(请求对象)传入到Engine容器。
tomcat同时支持网络IO方式:同步阻塞(BIO), 同步非阻塞(NIO), 异步非阻塞(NIO2/AIO), 通用IO操作规范(APR), 其中Tomcat8.5之后抛弃对BIO的支持。
》Mapper组件
路径映射组件,也可以称之路径路由。通过解析url的地址,映射到具体serlvet类。
》CoyoteAdaptor组件
Connector组件与Engine组件连接的适配器。

Container

tomcat容器的统称,tomcat中有4个级别的容器,分别是Engine,Host,Context, Wrapper。Connector组件接收与处理客户端发起的请求,将处理结果传入tomcat容器,以求进一步处理。如下图:
简单流程图

Engine

顶层的容器, 代表一个全局 Servlet 引擎,负责接收connector包装的请求。每一service只能有一个Engine组件,而Engine组件可以包含多个Host组件。除了Host组件外,Engin还包含其他组件
》Listener:监听tomcat生命周期中关于Engine容器相关的工作, 比如创建,销毁等
》AccessLog组件:记录客户端访问日志
》Cluster组件:提供集群功能
》PipeLine组件:Engine容器对请求进行处理管道
》Realm组件:提供Engine容器级别的权限校验信息对象

Host

Engine容器的子容器,表示一个虚拟主机。是根据URL地址中的主机(IP/域名,比如localhost)抽象而来。一个Engine容器可以包含若干个Host容器,而一个Host容器可以包含若干跟context容器。其他组件跟Egine子组件一样,这里不多说

Context

对应一个web应用程序,是对于web项目的抽象,负责将web项目所有组件转成tomcat能操作的容器组件。包含多个Wrapper组件。
》Loader组件:Web应用加载器,用于加载web资源。
》Manager组件:会话管理器,管理部署到tomcat中的web容器会话。比如维护会话的生成、更新、销毁
》NamingResource组件:命名资源,完成Tomcat server.xml和web应用context.xml资源与属性的映射
》Mapper组件:Servlet映射器

Wrapper

tomcat中最下的容器对象,与之对应的是servlet, 一个Wrapper对应一个Servlet。它包含3个子组件:
》Servlet组件:web项目处理逻辑的servlet
》ServletPool组件:Servlet对象池,如果servlet实现SingleThreadModel接口时,Wrapper会创建一个Servlet对象池,保证Servelt访问线程安全。
》PipeLine组件:Wrapper容器对请求进行处理管道

以一个案例来总结

假设在tomcat中部署一个web项目:

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <GlobalNamingResources>...</GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm"></Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />        
          <Context path="" docBase="xxx/helloApp/webapp" > 
        </Context>                                                          
      </Host>
    </Engine>
  </Service>
</Server>

1>在tomcat根/conf/server.xml文件上配置一个

 <Context path="" docBase="xxx/helloApp/webapp" ></Context>

2>启动tomcat,Connector组件接收器(Accoptor)监听server.xml配置的8080端口,当然其他端口(8009)也监听。目前关注8080端口

3>在浏览器地址栏中输入http://localhost:8080/hello 回车

4>浏览器发起请求(发起socket连接),Accoptor接收器接收到连接,将连接封装到任务线程,丢到线程池(Executor)处理.

5>线程池(Exceutor)发现任务队列中有线程任务, 马上可以执行响应任务,
1) 先判断发起连接的协议(Protocol):此处为Http1.1协议
2) 根据协议调用不同连接处理组件(Processor):此处调用的是Http11Processor组件
Http11Processor 组件开始解析HTTP请求头,请求行,请求体,将解析结果封装成Request对象,传入到容器中。

6>Mapper组件根据HTTP请求行中URL属性以及Host请求头,匹配哪个Host容器,哪个Context容器,哪个Wraper容器处理请求。这一步结合项目配置的web.xml文件可以找到具体的servlet

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>cn.wolfcode.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

7>CoyoteAdaptor组件是Connector跟Container连接桥梁,将前几步创建的请求Request对象与响应Response对象传到Engine容器,调用Engine容器的Pipeline管道执行其他额外的操作。

8>Engine容器执行容器中定义的各种Pipeline管道,最后执行Host容器的Pipeline管道,将Reqeust跟Response对象传入到Host容器。

9>Host容器执行容器中定义的各种Pipeline管道,最后执行Host容器的Pipeline管道,将Reqeust跟Response对象传入到Context容器。

10>Context容器执行容器中定义的各种Pipeline管道,最后执行Host容器的Pipeline管道,将Reqeust跟Response对象传入到Wrapper容器。
11>Wrapper容器调用其对应的Servlet中的service方法,处理请求逻辑,然后将结果响应给浏览器。
下面是上述请求的草图:

请求流程图


猜你喜欢

转载自blog.csdn.net/wolfcode_cn/article/details/80900720