Engine没有父容器, 调用 setParent方法时将会报错,添加子容器也只能是 Host 类型的。一个 Engine代表一个完整的 Servlet 引擎,接收来自Connector的请求,并决定传给哪个Host来处理。
Host容器是Engine的子容器,一个Host在Engine中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这些应用
,并且标识这个应用以便能够区分它们。每个虚拟主机对应一个域名,不同Host容器接受处理不同域名的请求。
Context表示一个Web应用程序,它代表Servlet的Context,它具备了Servlet运行的基本环境。在Tomcat的世界里,一个Host容器代表一个虚拟机资源,Context容器代表一个应用,所谓的部署器就是能够把Context容器添加进Host容器中去的一个组件。Context的配置文件中有个reloadable属性,当这个reloadable设为true时,war被修改后Tomcat会自动重新加载这个应用,在 ContainerBase.backgroundProcess()实现。
Context context =
new StandardContext();
Host host = new
StandardHost();
host.addChild(context);
Wrapper表示一个Servlet。它负责管理一个Servlet,包括Servlet的装载、初始化、执行以及资源回收。Wrapper是最底层的容器,它没有子容器了,所以调用它的addChild将会报错。Wrapper的实现类是StandardWrapper,StandardWrapper还实现了拥有一个Servlet初始化信息的ServletConfig。Wrapper是对一个Servlet的包装,所以它的基础阀内部调用的过滤器链的doFilter方法和Servlet的service方法。
| 方法 | 描述 |
| init | 在第一次请求该Servlet(默认)或容器启动时,Servlet容器就会调用init(),且只调用一次 |
| service | 每次请求Servlet都会调用该方法 |
| destroy | 销毁Servlet时(卸载应用/关闭容器时),调用该方法 |
StandardEngine、StandardContext、StandardHost、StandardWrapper都继承 了ContainerBase类,
ContainerBase内部类ContainerBackgroundProcessor
会周期的执行 run(), 它是运行在一个后台线程中, run()会周期调用所有容器的 backgroundProcess 方法
ContainerBase.backgroundProcess()方法:
- cluster
Catalina的集群组件,通过cluster组件可以实现集群应用部署,多个Tomcat实例,不需要每个都分别部署应用,只需要在某个实例上部署,整个集群中的各个实例都会自动同步应用进行部署。 它监听指定文件夹下有没有新增的war包或者文件是新增的还是修改的已决定来重新部署和通知其他tomcat集群成员
- loader
- manager
Catalina中的Session管理器,
主要的功能是将过期会话(session)置为无效
- realm
- pipeline
ContainerBase.startInternal()方法:
- Cluster.start()、Realm.start()
- for循环对每个子容器启动一个线程,多线程效率高,startStopExecutor.submit(new StartChild(children[i]))
- 启动管道和Valve阀链表,pipeline.start()
- 设置生命周期为start
- 启动Daemon线程ContainerBackgroundProcessor,该线程定期循环(while)调用
backgroundProcess()
ContainerBase.backgroundProcess()方法:
- Cluster、
Realm、Pipeline的backgroundProcess()
- fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null)
StandardContext.backgroundProcess()方法:
- Loader.backgroundProcess():Context.reload()
- Manager.backgroundProcess():Session.processExpires()
- WebResourceRoot.backgroundProcess():Cache.backgroundProcess()、StandardRoot.gc()
- InstanceManager.backgroundProcess()
- ContainerBase.backgroundProcess()
- ContainerBase.backgroundProcess():
- JspServlet.periodicEvent():jsp生成的servlet定期进行检查
责任链设计模式:
每个容器都有一个pipeline模块和valve模块,在容器对象构造时自动生成。pipeline是每个容器的逻辑总线,pipeline按照配置的顺序加载各个valve。 valve存放的方式并非统一存放在pipeline中,而是一个链表一个接着一个。
Connector的Adapter将request和response传给Container的管道,从Engine的管道一路处理到Wrapper的管道,Wrapper再将response一路返回给Engine,然后Engine将response返回给Connector,Connector再返回给用户浏览器。
详见org.apache.catalina.connector.CoyoteAdapter的service()方法 。
四个container相当于四个生产线(Pipeline),四个Pipeline都这么干,直到最后的。
StandardWrapperValve拿到资源开始调用servlet。完成后返回来,一步一步的valve按照刚才丢生产原料是的顺序的倒序一次执行。如此才完成了tomcat的Pipeline的机制。所有的vavle执行完毕后,整个响应的也就结束了。
pipeline的实现类StandardPipeline:
public StandardPipeline(Container container) {
super();
setContainer(container);
}@Override
public Container getContainer() {
return this.container;
}@Override
public void setContainer(Container container) {
this.container = container;
}@Override
public Valve[] getValves() {
List<Valve> valveList = new ArrayList<>();
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
valveList.add(current);
current = current.getNext();
}
return valveList.toArray(new Valve[0]);
}@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}@Override
protected synchronized void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
setState(LifecycleState.STARTING);
}@Override
protected synchronized void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
// Stop the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).stop();
current = current.getNext();
}
}@Override
protected void destroyInternal() {
Valve[] valves = getValves();
for (Valve valve : valves) {
removeValve(valve);
}
}@Override
public boolean isAsyncSupported() {
Valve valve = (first!=null)?first:basic;
boolean supported = true;
while (supported && valve!=null) {
supported = supported & valve.isAsyncSupported();
valve = valve.getNext();
}
return supported;
}Valve在处理完自己的事情以后,只需要将工作委托给下一个和自己在同一管道的阀门。
getNext().invoke(request, response)@Override
public void addValve(Valve valve) {
// Validate that we can add this Valve
if (valve instanceof Contained)
((Contained) valve).setContainer(this.container);
// Start the new component if necessary
if (getState().isAvailable()) {
if (valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.addValve: start: ", e);
}
}
}
// Add this Valve to the set associated with this Pipeline
if (first == null) {
first = valve;
valve.setNext(basic);
} else {
Valve current = first;
while (current != null) {
if (current.getNext() == basic) {
current.setNext(valve);
valve.setNext(basic);
break;
}
current = current.getNext();
}
}
container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}@Override
public void removeValve(Valve valve) {
Valve current;
if(first == valve) {
first = first.getNext();
current = null;
} else {
current = first;
}
while (current != null) {
if (current.getNext() == valve) {
current.setNext(valve.getNext());
break;
}
current = current.getNext();
}
if (first == basic) first = null;
if (valve instanceof Contained)
((Contained) valve).setContainer(null);
if (valve instanceof Lifecycle) {
// Stop this valve if necessary
if (getState().isAvailable()) {
try {
((Lifecycle) valve).stop();
} catch (LifecycleException e) {
log.error("StandardPipeline.removeValve: stop: ", e);
}
}
try {
((Lifecycle) valve).destroy();
} catch (LifecycleException e) {
log.error("StandardPipeline.removeValve: destroy: ", e);
}
}
container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}ValveBase是负责衔接各个管道(Pipeline)的,它负责将请求传递给下个管道的第一个阀门处理,它是每个管道中最后一个阀门,可以说基础阀是两个容器之间的桥梁。 基础阀的作用就是连接当前容器的下一个容器(通常是自己的子容器)。
@Override
public void setBasic(Valve valve) {
// Change components if necessary
Valve oldBasic = this.basic;
if (oldBasic == valve)
return;
// Stop the old component if necessary
if (oldBasic != null) {
if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
try {
((Lifecycle) oldBasic).stop();
} catch (LifecycleException e) {
log.error("StandardPipeline.setBasic: stop", e);
}
}
if (oldBasic instanceof Contained) {
try {
((Contained) oldBasic).setContainer(null);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
}
// Start the new component if necessary
if (valve == null)
return;
if (valve instanceof Contained) {
((Contained) valve).setContainer(this.container);
}
if (getState().isAvailable() && valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.setBasic: start", e);
return;
}
}
// Update the pipeline
Valve current = first;
while (current != null) {
if (current.getNext() == oldBasic) {
current.setNext(valve);
break;
}
current = current.getNext();
}
this.basic = valve;
}@Override
public Valve getBasic() {
return this.basic;
}StandardEngineValve:获取对应的站点(Host)
host.getPipeline().getFirst().invoke(request, response)StandardHostValve:
context.getPipeline().getFirst().invoke(request, response)StandardContextValve
- Request请求路径校验,不能WEB-INF和META-INF目录下的资源
- 根据Request选择合适的Wrapper
- Response.sendAcknowledgement(),TCP/IP协议中,如果接收方成功的接收到数据,会回复一个ACK数据
- wrapper.getPipeline().getFirst().invoke(request, response)
StandardWrapperValve
每个Wrapper都封装着一个servlet,所以Wrapper和servlet有着很深的联系。StandardWrapper里会加载他代表的servlet并创建实例(即调用它的init方法),但不会调用servlet的service方法。StandardWrapperValve会调用sertvlet的service方法,其具体执行顺序如下
- 分配一个Servlet实例,因为StandardWrapper负责加载servlet,所以也是从Wrapper中获取servlet。
- 执行Servlet相关的所有过滤器:ApplicationFilterChain可以看做是一个“过滤器链”,StandardWrapperValve中的invoke会先创建该类的实例,然后调用它的doFilter方法,即从该链中的第一个过滤器开始调用。如果执行到了最后一个过滤器,就开始调用Servlet的service方法。
- 关闭过滤器链
- 通知Wrapper,重新委派处理完毕的servlet。
请求通过管道流转到Wrapper容器的管道,经过若干阀门后到达基础阀门StandardWrapperValve,它将创建一个过滤器链Application FilterChain对象,创建时过滤器链对象做如下逻辑处理:
- 从Context容器中获取所有过滤器的相关信息
- 通过URL匹配过滤器,匹配的加入到过滤器中
- 通过Servlet名称匹配过滤器,匹配的加入到过滤器链中
创建ApplicationFilterChain对象后,StandardWrapperValve将调用它的doFilter方法,它就会开始一个一个调用过滤器,请求被一层层处理,最后才调Servlet处理。至此,针对某个请求,过滤器链将Context中所有过滤器中对应该请求的过滤器串联起来,实现过滤器功能。
根据请求资源的不同种类,可以把Servlet分成三种类别,比如请求可能访问一个普通的Servlet,也可能访问一个JSP页面,也可能访问一个静态资源。根据对这些不同类别的处理方式,可以分为三种Servlet。如上图所示,一个请求到达Tomcat后将由URI映射器根据URI进行建模,它会计算出该请求该发往哪个Host容器的哪个Context容器的哪个Wrapper处理,在路由到Wrapper容器时会通过一定的算法选择不同的Servlet进行处理。比如,普通Servlet请求则路由到普通Servlet,JSP页面则路由到JspServlet,而静态资源则路由到DefaultServlet。
Tomcat的客户端请求由管道处理,最后会通过Wrapper容器的管道,这时它会调用Servlet实例的service方法进行逻辑处理,处理完后响应客户端。整个处理由Tomcat的Executor线程池的线程处理,而线程池的最大线程数是有限制的,所以这个处理过程越短,就能越快地将线程释放回线程池。但如果Servlet中的处理逻辑耗时越长,就会导致长期地占用Tomcat的处理线程池,影响Tomcat的整体处理能力。
Service的Mapper组件:Tomcat在处理请求时对请求的路由分发全由Mapper组件负责,提供请求路径的路由映射,根据某个请求路径通过计算得到相应的Servlet(Wrapper)。如果要将整个tomcat容器中所有的web项目以能够以Servlet级别组织起来,需要一个多层级的类似Map结构的存储空间。如下图,以Mapper作为映射的入口,按照容器等级首先会包含了N个Host容器的引用,然后每个Host会有N个Context容器的引用,最后每个Context容器包含N个Wrapper容器的引用。
MappedHost—>MappedContext—>MappedWrapper
<Host name="www.baidu.com" appBase="webapps" autoDeploy="true">
<Context path="/apoms" docBase=" /usr/local/apoms"/>
</Host>
CATALINA-HOME/webapps/中的应用启动是放到线程池中进行异步运行的,
在org.apache.catalina.startup.HostConfig#deployApps()方法中。
应用的启动在StandardHost#start的时候,通过Lifecycle.START_EVENT这个事件的监听器HostConfig进行进一步的启动。