一、Tomcat整体架构介绍
Tomcat是一个开源的轻量级web应用服务器。整体架构如下:
- Tomcat中最顶层的容器是Server,即代表一个Tomcat服务器,一个Server中可以有多个Service,对外提供不同的web服务。
- Service是对Connector和Container的封装,一个Service中有一个或多个连接器,和一个Container容器。
- 连接器Connector负责监听端口接收请求并按照设定的协议进行解析数据,将解析后的请求交给容器处理。连接器之所以设计为支持多个,主要是为了方便客户端可以通过不同的协议来发送请求,一个Connector可监听一个端口 设定一种IO模式(BIO、NIO、APR)和一种应用层协议(HTTP、HTTPS和AJP等)。
- 容器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对象后,传递到容器执行处理的过程如下:
- Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、Wrapper容器处理请求。
- CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的Request对象转换为标准的ServletRequest对象传递到Engine容器中,调用 Pipeline。
- Engine容器的管道开始处理,管道中包含若干个Valve、每个Valve负责部分处理逻辑。执行完Valve后会执行基础的 Valve–StandardEngineValve,负责调用Host容器的Pipeline。
- Host容器的管道开始处理,流程类似,最后执行 Context容器的Pipeline。
- Context容器的管道开始处理,流程类似,最后执行 Wrapper容器的Pipeline。
- 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>
其中定义<Server port=“8005” shutdown=“SHUTDOWN”>这会让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>
<Context>用于配置一个虚拟路径,这样我们开发项目就不用都拷到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>
参考:
- Tomcat服务部署及优化
- Tomcat处理请求流程
- Tomcat – 请求处理流程