一、Tomcat整体架构介绍

Tomcat是一个开源的轻量级web应用服务器。整体架构如下:

  1. Tomcat中最顶层的容器是Server,即代表一个Tomcat服务器,一个Server中可以有多个Service,对外提供不同的web服务。
  2. Service是对Connector和Container的封装,一个Service中有一个或多个连接器,和一个Container容器。
  3. 连接器Connector负责监听端口接收请求并按照设定的协议进行解析数据,将解析后的请求交给容器处理。连接器之所以设计为支持多个,主要是为了方便客户端可以通过不同的协议来发送请求,一个Connector可监听一个端口 设定一种IO模式(BIO、NIO、APR)和一种应用层协议(HTTP、HTTPS和AJP等)。
  4. 容器Container负责处理连接器传递过来的已经解析封装好的请求,通过层层处理最终调用请求路径匹配的filter / servlet实例来处理请求,处理完毕后将结果原路返回由Connector发出。

二、连接器Connector详解

Connector中使用ProtocolHandler来封装不同的IO模式和应用协议组合,例如NIO模式和HTTP1.1协议对应的就是Http11NioProtocol类。在配置文件中配置Connector的protocol属性值为Http11NioProtocol即使其成为支持NIO和HTTP1.1的连接器。

<Connector port="8080" executor="tomcatThreadPool" protocol="org.apache.coyote.http11.Http11NioProtocol"redirectPort="8443" enableLookups="false" URIEncoding="UTF-8" maxKeepAliveRequests="500" />

Tomcat8前默认为BIO模式,Tomcat8.0起默认为NIO模式,并在Tomcat8.5起移除了BIO模式

在ProtocolHandler中,EndPoint类负责Socket请求的收发,Processor类负责从Socket中解析出对应协议格式的数据并封装为Request 或者反向。然后通过适配器Adapter将Request转换为标准的ServletRequest传递给Servlet容器进行处理。这样每个模块只负责必要的部分,实现功能模块的高内聚和低耦合,

1. EndPoint

在BIO实现的Connector中,处理请求的主要实体是JIoEndpoint对象。JIoEndpoint维护了Acceptor和一个线程池:Acceptor负责接收socket,然后从线程池中找出空闲的线程处理socket,如果线程池没有空闲线程,则Acceptor将阻塞。

在NIO实现的Connector中,处理请求的主要实体是NIoEndpoint对象。在NIoEndpoint中Acceptor接收socket后,不是直接放到线程池中处理,而是先将socket封装为一个Channel放进Poller的通道队列中,而Poller是实现NIO的关键,在Poller中,维护了一个Selector选择器对象,Poller从通道队列中取出socket后注册到该Selector中,然后通过Selector轮询,找出其中可读的socket,并放到线程池中进行后续处理。


Acceptor和Poller默认都是单线程的,而线程池默认使用的Tomcat自己定制的线程池,在JDK的线程池ThreadPoolExecutor中只有当任务等待队列满了才会启动非核心线程,但是在TomcatThreadPool中当核心线程数满了之后就会启动非核心线程,以便请求能够快速得到响应。

二、容器Container详解

容器中有如下四个组件,并且这四个组件是具有层次关系的:一个Engine下可以有多个Host,一个Host下可以有多个Context,一个Context下可以有多个Wrapper,一个Wrapper下可以有多个Servlet实例对象。

  • Engine:一个容器Container中有一个Engine

  • Host:一个Host表示一个虚拟服务器,可以给每个Host配置一个域名

  • Context:一个Context就是一个应用,一个项目

  • Wrapper:一个Wrapper是对Servlet的包装,一个Wrapper下可以有多个Servlet实例对象。

    在连接器生成请求的Request对象后,传递到容器执行处理的过程如下:

  1. Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、Wrapper容器处理请求。
  2. CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的Request对象转换为标准的ServletRequest对象传递到Engine容器中,调用 Pipeline。
  3. Engine容器的管道开始处理,管道中包含若干个Valve、每个Valve负责部分处理逻辑。执行完Valve后会执行基础的 Valve–StandardEngineValve,负责调用Host容器的Pipeline。
  4. Host容器的管道开始处理,流程类似,最后执行 Context容器的Pipeline。
  5. Context容器的管道开始处理,流程类似,最后执行 Wrapper容器的Pipeline。
  6. Wrapper容器的管道开始处理,流程类似,最后在执行完过滤链FilterChain后执行对应Servlet实例的处理方法

三、配置文件server.xml和web.xml

1. server.xml

server.xml是Tomcat的核心配置文件,定义整个Tomcat服务器的架构。其默认的主要配置如下:

<Server port="8005" shutdown="SHUTDOWN"> <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"> <Host name="localhost"appBase="webapps"unpackWARs="true" autoDeploy="true"> <Context ><Context /> </Host></Engine></Service></Server>

其中定义这会让Tomcat启动一个server实例(即一个JVM),它监听在8005端口以接收“SHUTDOWN”命令,如果接收到了就会关闭Tomcat。各Server的定义不能使用同一个端口,这意味着如果在同一个机器上启动了多个Tomcat服务器,必须配置它们使用不同的端口。

2. web.xml

上述默认Host配置中appBase=”webapps“表示默认应用,Tomcat目录下:d:\tomcat\webapps 这样的路径,实际可以指定多个自定义的Context应用:

<Host name="localhost" unpackWARs="true" autoDeploy="true"><Context path="/js" docBase="E:\eclipse\java_web\jspPrime\webapps" reloadable="true"/><Context path="/word" docBase="D:\apache-tomcat-7.0.35\webapps"/></Host>

用于配置一个虚拟路径,这样我们开发项目就不用都拷到webapps下了,而是指定一个虚拟路径,这个虚拟路径的名字是path=“**” 而实际绝对路径是 docBase所指的。

在Context应用目录下,需要有一个web.xml文件,用于配置Filter、Servlet类等的路径及其匹配路径。一个样例如下

<web-app><display-name>MyWebapp</display-name><filter><filter-name>global</filter-name><filter-class>com.xxxx.MyFilter</filter-class><init-param><param-name>test</param-name><param-value>1</param-value></init-param></filter><filter-mapping><filter-name>global</filter-name><url-pattern>/*</url-pattern></filter-mapping><servlet><servlet-name>MyHttpServlet</servlet-name><servlet-class>com.xxxx.MyHttpServlet</servlet-class><init-param><param-name>testP</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>MyHttpServlet</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping></web-app>

参考:

  1. Tomcat服务部署及优化
  2. Tomcat处理请求流程
  3. Tomcat – 请求处理流程