tomcat的启动是通过Bootstrap类的main方法(tomcat6开始也可以直接通过Catlina的main启动)
Bootstrap的启动
Bootstrap的main方法先new了一个自己的对象(Bootstrap),然后用该对象主要执行了四个方法:
init();
setAwait(true);
load(args);
start();
init():初始化了ClassLoader(类加载器,没它的话后面就没法加载其他类了),然后用ClassLoader创建了Catlina实例。
setAwait(true),load(args),start();这三个方法实际上都是通过反射调用的Catlina实例中所对应的这三个方法。
Catlina的启动
首先Catlina中的setAwait(true)方法:
这个方法设置的值(true或false),是留给后面用的。当通过后面的start()方法启动完服务器后,会检查这个值为true还是false,如果为true,调用Catlina的await()方法,tomcat继续开着。如果为false,则调用Catlina的stop()方法,关闭tomcat。
然后是Catlina中的load(args)方法:
Catalina将利用Digest解析server.xml(server的配置文件,server是最顶层容器),并根据server的配置文件创建server实例。
然后调用server实例的init()方法,进行server的初始化。
Server初始化时,又会调用其内部的Service的init方法(server只有一个,但service有多个)。
大体流程如下图:
然后是Catlina中的start()方法:
Catlina的start()方法主要调用了server的start()方法来启动服务器,然后server的start()方法再调用多个service的start()方法。
大体流程如下图:
Server的启动
由上面Catlina的启动我们知道了,
Catlina中的load(args)方法使得Server进行了初始化,
Catlina中的start()方法使得Server也调用了start方法。
Server是一个接口,它的默认实现类是StandardServer,所以上面和之后我们操作的Server实际上是操作的StandardServer。
StandardServer继承自LifecycleMBeanBase,而LifecycleMBeanBase又继承自LifecycelBean。
根据之前的分析我们知道,StandardServer主要要调用两个方法:init()和start()。
而这两个方法都不在StandardServer中,实际上都在StandardServer的父类LifecycelBean中。而LifecycelBean中的这两个方法,实际上由“回头”调用了StandardServer中的initInternal()和startInternal()方法。
也就是StandardServer调用了init()实际上是调用了自己的initInternal()方法。
StandardServer调用了start()实际上是调用了自己的startInternal()方法。
initInternal()方法和startInternal()方法“分别循环调用了每一个service的init()方法和start()方法”。
除以上方法外,Standard还有addService()方法用来添加service,和remove()方法用来删除service。
另外Standard还拥有await()方法,用法在上面Catlina的启动中讲了。
Service的启动
一个Serve中可以有多个service,Service也是接口,Service接口的默认实现类是StandardService,所以上面和后面讨论的service实际上是在操作StandardService。
StandardService也继承于LifecycleMBeanBase,而LifecycleMBeanBase继承于LifecycelBean,LifecycelBean中存在init()和start()方法。
所以Server循环调用每个StandardService的init()和start()方法,其实是调用的StandardService的父类LifecycelBean中的init()和start()方法,然后init()和start()方法在“回头调用”StandardService中的initInternal()方法和startInternal()方法
也就是说Server循环调用每个StandardService的init()和start()方法,最后执行的是每个Service的initInternal()方法和startInternal()方法。
每一个service都是一个对外提供服务的组件,每一个Service中都包含:一个Container+多个Connector,所以service中的方法,都是用来操作这两种类的。
initInternal()方法:
下面是initInternal源码:
protected void initInternal() throws LifecycleException { super.initInternal(); if(this.container != null) { this.container.init(); } Executor[] arr$ = this.findExecutors(); int arr$1 = arr$.length; int len$; for(len$ = 0; len$ < arr$1; ++len$) { Executor i$ = arr$[len$]; if(i$ instanceof JmxEnabled) { ((JmxEnabled)i$).setDomain(this.getDomain()); } i$.init(); } this.mapperListener.init(); Object var11 = this.connectorsLock; synchronized(this.connectorsLock) { Connector[] var12 = this.connectors; len$ = var12.length; for(int var13 = 0; var13 < len$; ++var13) { Connector connector = var12[var13]; try { connector.init(); } catch (Exception var9) { String message = sm.getString("standardService.connector.initFailed", new Object[]{connector}); log.error(message, var9); if(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new LifecycleException(message); } } } } }
首先,调用Container的init()方法,
再调用mapperListener的init()方法(mapperListener是用来监听container的变化的),
再调用“多个”excutor的init()方法(excutor是用来在connector中管理线程池的),
再调用Connctor的init()方法。
startInternal()方法:
与上同理,也是分别调用四种类的start()方法。
总结一下,Server和Service的关系大致如下图(需要说明的是,下图中每个类或接口中所包含的方法我并没有全写出来,只写了与流程相关的几个):
Lifecycle接口
将到这里应该提一下Lifecycle接口,该接口的默认实现就是LifecycleBean类。
通过上面提到Server和Service子类我们发现他们都继承了LifecycleBean类,并调用了该类的init()和start()方法。
实际上凡是拥有“生命周期”的组件都实现了Lifecycle接口,Lifecycle接口的作用就是进行“生命周期的规定”。
该接口主要做了以下四件事:
一、定义了13个String常量,这些常量被用于定义“LifecycleEvent事件”的type属性中 。这样设计的好处是,只需要发送一种类型事件LifecycleEvent,而根据里面type属性的值的不同,来判定是什么事件。(否则13种事件就得定义13中事件类)。
二、定义了三个管理监听的方法,分别用来添加、查找、删除LifecycleListener类型的监听器。
三、定义了四个生命周期方法:init()、start()、stop()、destroy(),用于执行生命周期各个阶段的操作。
四、定义了用来获取当前状态的两个方法。
LifecycleBase实现类
lifecycleBase是Lifecycle接口的默认实现类,所有拥有生命周期的组件都直接或间接的继承自lifecycleBase类,lifecycleBase为lifecycle接口中的各个方法提供了默认实现。
三个管理监听的方法:
这三个方法分别是addLifecycleListener(添加监听器)、findLifecycleListeners(查找监听器)、removeLifecycleListener(删除监听器)。
监听器的管理专门使用了一个“LifecycleSupport类”来完成。
这三个方法都调用了LifecycleSupport中的同名方法。
LifecycleSupport中通过一个数组来保存现有个监听器。
另外LifecycleSupport中还定义了处理LifecycleEvent时间的方法。
四个生命周期方法:
主要就是init()和start()方法,在调用方法之前会先判断当前状态与要调用的方法是否匹配,如果不匹配则会执行相应的方法使其匹配(如在低啊用init之前先调用了start,这是就会先执行init)。
之后会调用相应的模板方法(initInternal()和startInternal())让子类具体执行init和start。
我们先看一下init()方法的源码:
public final synchronized void init() throws LifecycleException { if(!this.state.equals(LifecycleState.NEW)) { this.invalidTransition("before_init"); } this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false); try { this.initInternal(); } catch (Throwable var2) { ExceptionUtils.handleThrowable(var2); this.setStateInternal(LifecycleState.FAILED, (Object)null, false); throw new LifecycleException(sm.getString("lifecycleBase.initFail", new Object[]{this.toString()}), var2); } this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false); }
可以看出,init()方法先将当前状态设置成了INITIALIZING,然后执行的是具体实现类的initInternal()方法。
再看看start()的源码:
public final synchronized void start() throws LifecycleException { if(!LifecycleState.STARTING_PREP.equals(this.state) && !LifecycleState.STARTING.equals(this.state) && !LifecycleState.STARTED.equals(this.state)) { if(this.state.equals(LifecycleState.NEW)) { this.init(); } else if(this.state.equals(LifecycleState.FAILED)) { this.stop(); } else if(!this.state.equals(LifecycleState.INITIALIZED) && !this.state.equals(LifecycleState.STOPPED)) { this.invalidTransition("before_start"); } this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null, false); try { this.startInternal(); } catch (Throwable var2) { ExceptionUtils.handleThrowable(var2); this.setStateInternal(LifecycleState.FAILED, (Object)null, false); throw new LifecycleException(sm.getString("lifecycleBase.startFail", new Object[]{this.toString()}), var2); } if(this.state.equals(LifecycleState.FAILED)) { this.stop(); } else if(!this.state.equals(LifecycleState.STARTING)) { this.invalidTransition("after_start"); } else { this.setStateInternal(LifecycleState.STARTED, (Object)null, false); } } else { if(log.isDebugEnabled()) { LifecycleException t = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}), t); } else if(log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()})); } } }
由此可看到start方法判断了当前状态,如果状态正确执行的起始是子类的startInternal()方法
两个获取当前状态的方法:
在生命周期的相应方法中已经设置了state属性,这两个方法就是返回该属性。
Connector
Connector的主要任务是负责处理浏览器发送过来的请求,并创建一个Request和Response的对象用于和浏览器交换数据,然后产生一个线程用于处理请求,Connector会把Request和Response对象传递给该线程,该线程的具体的处理过程是Container容器的事了。Container就是Servlet的容器,Container处理完后会吧结果返回给Connector,最后Connector使用socket将处理结果返回给客户端,整个请求处理完毕。
Connector的底层是使用socket进行连接,Request和Response时按照http协议(协议可选)来封装,所以Connector同时实现了TCP/IP和HTTP协议。
很明显Connector应该可以有多个,因为一次请求对应一个Connector,统一时间可能有多个请求。
Connector可以处理不同协议类型的请求,默认处理的是HTTP1.1协议。
Connector中使用ProtocolHandler来具体处理请求,不同的ProtocolHandler代表不同协议的请求类型。
ProtocolHandler中又包含三个重要的组件:
Endpoint,用于处理底层socket网络连接。
Processor,用于将socket接收到的socket封装成request。
Adapter,用于将requst交给Container进行处理。
具体执行顺序如下
一、之前讲过了,Catlina会调用load方法,根据server.xml配置文件创建Server对象,之后Server由调用init()创建service,service再调用Connector的init方法创建并初始化。
所以Connector是在Catlina会调用load方法时创建的。
二、Conntctor首先执行的是初始化init,Conntctor的初始化主要是用来初始化ProtocolHandler。
所以Conntctor执行init后:
1、先创建一个Adapter,并设置到ProtocolHandler中(这个Adapter后面要用,也就是上面所提到的用于将requst交给Container进行处理。)
2、调用ProtocolHandler的init方法,让它进行初始化。
3、调用mapperListener的init方法初始化(mapperListener作用是用来监听容器,容器发生变化会被通知)
三、刚刚上面提到了ProtocolHandler的init方法被调用了,我们在看看ProtocolHandler初始化都做了什么。
整个ProtocolHandler结构如下图:
ProtocolHandler的初始化方法被实现于“AbstractProtocol抽象类”中。
该抽象类有两种子抽象类:AbstractAjpProtocol和AbstractHttp11Protocol,分别对应了两种不同的请求协议。
所以最终我们初始化的步骤其实是在AbstractHttp11Protocol抽象类中执行(当协议为HTTP时)。
配置文件server.xml中可以指明需要创建的ProtocolHandler类型,默认是处理HTTP1.1协议的ProtocolHandler,之后我们真正创建的就是AbstractHttp11Protocol抽象类的某一个子类(如Http11NioProtocol)
AbstractHttp11Protocol抽象类的初始化主要是调用了Endpoint的init初始化。
四、endpoint的init方法在抽象父类AbstractEndpoint中,是个模板方法,实际上调用的是子类NioEndpoint里面的bind()方法。
这里的bind()方法主要作用是,检查Acceptor和poller的线程数量是否正确(必须大于等于1)。
提一下Acceptor和poller这两个类,Acceptor的作用是控制与tomcat建立连接的数量,但Acceptor只负责建立连接。socket内容的读写是通过Poller来实现的。
此时Connector的init初始化就完成了。
五、Connector的init方法执行完后,会被调用执行startInternal方法。
同理,此方法会调用protocolHandler.start()方法。
然后protocolHandler的start又会调用endpoint.start()方法。
endpoint的start方法在抽象父类AbstractEndpoint中,是个模板方法,实际上调用的是子类NioEndpoint里面的startInternal()方法。
该方法主要做了:
1、初始化一些属性
2、根据之前定义的Acceptor和poller的线程数量,来启动相应数量的Acceptor和poller。
其中poller会将请求交给SocketProcessor,而SocketProcessor的职责就是把具体的请求处理过程委派给Handler,handler会执行Processor接口里的process()方法,进行“对request的解析,包括请求头、请求行和请求体”。
而这个process()方法是在抽象父类AbstractHttp11Processor里实现的,
process()方法会先从socket里读取http请求数据,并解析请求头,构造Request对象和Response对象。
六、process()方法最后会调用Adapter的service()方法。
Adapter接口只有一个实现类CoyoteAdapter。
service()完成“请求行和请求体的解析”,并把解析出来的信息封装到Request对象和Response对象中,之后service()便将封装了Request以及Response对象的Socket传给Container容器了。
传输的代码是:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
即:先从Connector中获取service,然后从service中获取Container。接着再获取管道,再获取管道的第一个值Value,最后调用invoke()方法执行请求。
七、调用mapperListener的start方法。,该方法注册了已初始化的组件(在上面讲的Connector的init时初始化的mapperListener),然后为这些组件添加监听器,最后添加容器之间的映射关系。
此时Connector的startIntenal就完成了。
至此整个Connector启动完毕,大致的流程图如下(需要说明的是,下图中每个类或接口中所包含的方法我并没有全写出来,只写了与流程相关的几个,并且多处地方的子类选择不是唯一的,我画的该流程是基于获取HTTP1.1协议的请求):
Container
Container容器是子容器的父接口,所有的子容器都继承该接口。
Container容器还继承Lifecycle接口,这样就拥有了完整的生命周期,他的子容器也就拥有了完整的生命周期。
Container接口的实现子类为ContainerBase,
其下的四个子容器接口为:Engine、Host、Context、Wrapper。他们之间是逐层包含的,每个service只能有一个Engine,一个Engine能够含有多个Host,每个Host可以含有多个Context,每个Context能够含有多个Wrapper。
四个子容器接口也有各自的子实现类,这些实现类继承自ContainerBase。
4中子容器的作用:
1、Engine:接收Connector传来的request和response,选择可用的Host来处理当前请求管理Host)。
2、Host:代表一个虚拟主机,也是一个站点。外面以www.demo.com/test这个url为例,www.demo.com这个域名就代表一个Host,该域名对应的就是webapps目录所代表的站点。
3、Context:代表一个应用,还是以www.demo.com/test这个url为例,通过域名可以直接访问到ROOT根目录里面的应用就是主应用。webapps/test目录可以通过www.demo.com/test访问到,这个test就又是一个子应用。于是这就是两个Context(每一个应用对应一个Context)。
所以www.demo.com下的应用都属于该Host站点。而其他的域名又是其他Host站点。
4、Wrapper:每一个Wrapper封装一个servlet。
这里需要特殊说明的是Container的四个子容器的生命周期:
1、虽然四个子容器的共同父类ContainerBase定义了initInternal()和startInternal()方法,但子容器还是可以根据自身情况再添加内容。
2、ContainerBase的init()方法是service初始化的时候调用的。但四个子容器的init方法“并不是”在ContainerBase的init()方法中被循环调用。而是在执行start()方法时,通过状态判定来调用的init()(如果没初始化就初始化)。
3、Context和Wrapper是“动态添加的”,在站点目录下每放置一个war包,就会动态添加一个Context,在web.xml里每配置一个servlet,就可以动态添加一个Wrapper。所以子容器的start方法不仅仅在“tomcat启动的时候会被调用”,当"父容器ContainerBase添加某个子容器时",也会调用该子容器的start()方法。
Container的执行流程为两条:
第一条流程为:Service执行init()调用Container的init()方法(注意,此时Container并不会调用子容器的init()方法),然后Service执行start()时调用Container的start,Container的start再循环调用每一个子容器的start()方法。然后再调用管道的“生命周期管理”(管道不需要初始化,所以在init()中不会调用)。第一条流程图示如下:
Container的init()和start()方法执行内容如下:
一、Container的启动是通过init()和start()完成的。之前分析过这两个方法都会通过service来调用。
init()和start存在于LifecycelBean中,实际上最后调用的是ContainerBase中的initInternal()和startInternal()方法。
ContainerBase的initInternal方法主要是初始化了ThreadPoolExecutor类(startStopExecutor属性),用于管理启动和关闭的线程。
ThreadPoolExecutor继承自Executor用于管理线程,这里就不过多介绍了。
代码如下:
protected void initInternal() throws LifecycleException { LinkedBlockingQueue startStopQueue = new LinkedBlockingQueue(); this.startStopExecutor = new ThreadPoolExecutor(this.getStartStopThreadsInternal(), this.getStartStopThreadsInternal(), 10L, TimeUnit.SECONDS, startStopQueue, new ContainerBase.StartStopThreadFactory(this.getName() + "-startStop-")); this.startStopExecutor.allowCoreThreadTimeOut(true); super.initInternal(); }
二、ContainerBase执行完initInternal后,会由于service的start的调用,执行ContainerBase的startInternal()方法。
代码如下:
protected synchronized void startInternal() throws LifecycleException { this.logger = null; this.getLogger(); //如果有Cluster和Realm,则调用他们的start方法。 Cluster cluster = this.getClusterInternal(); if(cluster != null && cluster instanceof Lifecycle) { ((Lifecycle)cluster).start(); } Realm realm = this.getRealmInternal(); if(realm != null && realm instanceof Lifecycle) { ((Lifecycle)realm).start(); } Container[] children = this.findChildren(); ArrayList results = new ArrayList(); //使用startStopExecutor调用新线程来启动每一个子容器 for(int fail = 0; fail < children.length; ++fail) { results.add(this.startStopExecutor.submit(new ContainerBase.StartChild(children[fail]))); } boolean var10 = false; Iterator i$ = results.iterator(); while(i$.hasNext()) { Future result = (Future)i$.next(); try { result.get(); } catch (Exception var9) { log.error(sm.getString("containerBase.threadedStartFailed"), var9); var10 = true; } } if(var10) { throw new LifecycleException(sm.getString("containerBase.threadedStartFailed")); } else { //调用管道的启动方法 if(this.pipeline instanceof Lifecycle) { ((Lifecycle)this.pipeline).start(); } //设置生命周期状态为STARTING状态。 this.setState(LifecycleState.STARTING); //调用threadStart方法启动后台线程 this.threadStart(); } }
startInternal()方法主要做了5件事:
1、如果有Cluster和Realm,则调用他们的start方法。
Cluster用于配置集群,在server.xml中可配置,它的作用是同步session。
Realm是tomcat的安全域,可以用来管理资源的访问权限。
2、使用startStopExecutor调用新线程来启动每一个子容器(即调用他们的start()方法),具体启动过程是通过一个for循环对每个子容器启动一个线程(这样可以多个线程同时启动,效率高),并将返回的Future保存到一个List中,然后遍历每个Future并调用其get方法。(下面详细说)
3、调用管道中Value的start方法来启动管道。(下面详细说)
4、设置生命周期状态为STARTING状态。
5、调用threadStart方法启动后台线程,该线程是一个while循环,定期调用backgroundProcess方法做一些事情,间隔时间可通过属性设置,单位是秒,如果小于0就不启动后台线程了。
backgroundProcess()方法在ContainerBase、StandardContext、StandardWrapper中都有实现。
ContainerBase中的backgroundProcess():调用了Cluster、Realm和管道的backgroundProcess()方法。
StandardContext中的backgroundProcess():调用ContainerBase中的backgroundProcess(),还对Session过期和资源变化进行了处理。
StandardWrapper中的backgroundProcess():调用ContainerBase中的backgroundProcess(),还会对jsp生成的servlet定期进行检查。
至此Container的启动就完成了,接下来我们详细的说一下上面的四个子容器的启动,和管道的启动。
先来看看使用startStopExecutor调用新线程来启动每一个子容器(即调用他们的start()方法)时,每一个子容器都干了些啥。
由于每一个子容器的接口都继承自Container接口,而Container接口又继承自Lifecycle接口,所以每一个子容器都有完整的生命周期(都拥有start和init方法),所以调用各个子容器的start方法实际上就是调用他们各自实现类的startInternal()方法。
StandardEngine:
protected void initInternal() throws LifecycleException { this.getRealm(); super.initInternal(); }
由源码可看出init方法调用了getRealm(),作用是如果没有配置Realm,则使用默认的NullPealm。然后调用了父类ContainerBase中的initInternal方法。
protected synchronized void startInternal() throws LifecycleException { if(log.isInfoEnabled()) { log.info("Starting Servlet Engine: " + ServerInfo.getServerInfo()); } super.startInternal(); }
由源码可看出:startInternal()只是调用了父类的startInternal()方法。
StandardHost:
Host的默认实现类StandardHost没有重写initInternal方法,所以初始化时调用的是父类的相应方法。
startInternal代码如下:
protected synchronized void startInternal() throws LifecycleException { String errorValve = this.getErrorReportValveClass(); if(errorValve != null && !errorValve.equals("")) { try { boolean t = false; Valve[] valves = this.getPipeline().getValves(); Valve[] valve = valves; int len$ = valves.length; for(int i$ = 0; i$ < len$; ++i$) { Valve valve1 = valve[i$]; if(errorValve.equals(valve1.getClass().getName())) { t = true; break; } } if(!t) { Valve var9 = (Valve)Class.forName(errorValve).newInstance(); this.getPipeline().addValve(var9); } } catch (Throwable var8) { ExceptionUtils.handleThrowable(var8); log.error(sm.getString("standardHost.invalidErrorReportValveClass", new Object[]{errorValve}), var8); } } super.startInternal(); }
由此看到此方法就是查看管道中是否有ErrorReportValve,如果没有就将他添加进去。判断的方法就是遍历管道里面的所有value,然后通过每个value的名字进行判断。
StandardContext:
在startInternal中通过一个var25的boolean来判断是否进行下一步的处理,起始位true,当中途遇到问题后改为false,后面某些处理就不做了。
if(var25 && !this.listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); var25 = false; }
此处触发Listener(Listener定义在web.xml中)。
if(var25 && !this.filterStart()) { log.error(sm.getString("standardContext.filterFail")); var25 = false; }
此处触发web.xml中配置的filter过滤器。
if(var25 && !this.loadOnStartup(this.findChildren())) { log.error(sm.getString("standardContext.servletFail")); var25 = false; }
此处初始化了所有Servlet,即调用在web.xml中配置了load-on-startup的servlet的init方法初始化(load-on-startup的作用是标记容器是否在启动时加载此servlet)。
StandardWrapper:
StandardWrapper没有重写initInternal方法,其startInternal()源码如下:
protected synchronized void startInternal() throws LifecycleException { Notification notification; if(this.getObjectName() != null) { notification = new Notification("j2ee.state.starting", this.getObjectName(), (long)(this.sequenceNumber++)); this.broadcaster.sendNotification(notification); } super.startInternal(); this.setAvailable(0L); if(this.getObjectName() != null) { notification = new Notification("j2ee.state.running", this.getObjectName(), (long)(this.sequenceNumber++)); this.broadcaster.sendNotification(notification); } }
主要就做了三件事:
1、用broadcaster发送通知,只要用于JMX,
2、调用父类的startInternal方法
3、调用setAvailable方法,作用是设置Wrapper包含的所有servlet的有效的起始时间。
下面谈论管道的startInternal方法:
这里有必要先大致的说一下管道:
由源码可知管道是在ContainerBase中启动的,而四个子容器都继承了ContainerBase,所以四个自容器都有属于自己的管道。
而每一个管道里装的都是“Value”类型,Value是接口,Value的实现类用于进行各种各样的具体处理操作。
所谓管道就是指多个处理者(value)对某一个请求“依次”进行处理,请求交给第一个value处理完后,再交给第二个value处理·····,每一条管道的“最后一个Value”都会有点特殊,该Value会将请求发送给下一个管道,然后下一个管道拿到请求后,继续交给该管道内的第一个value处理·····
举例,如:
请求到达Engine后,实际上是传给了Engine的管道(因为Engine继承了Container,所以他拥有StandardPipeline类型的管道),然后管道中有一个Value first属性,该属性是一个链式结构,存放在管道中first里面的Value是该管道的第一个value,而该value内部存在指针,指向他的下一个value。
管道就能依靠头value来遍历该管道内的每一个value,让他们依次处理请求。
而Engine管道的最后一个value为StandardEngineValue类,该value会将请求发送给Host的管道。
总结一下,每一个子容器都有它的StandardPipeline管道,每一条管道中装有多个value,value中的 invoke方法用来进行具体的处理请求,每个管道中的最后一个value会将请求传给下一个子容器的管道。(感觉自己有点啰嗦了。。。 (`?ω?′))
回到管道的生命周期讲解,
在ContainerBase中调用管道的启动方法(该语句存在于startInternal中):
if(this.pipeline instanceof Lifecycle) { ((Lifecycle)this.pipeline).start(); }
Pipeline的实现类为StandardPipeline类。该类中相应的源码为:
protected synchronized void startInternal() throws LifecycleException { Valve current = this.first; if(current == null) { current = this.basic; } for(; current != null; current = current.getNext()) { if(current instanceof Lifecycle) { ((Lifecycle)current).start(); } } this.setState(LifecycleState.STARTING); }
首先调用了first属性赋值给了current,first属性里存储的是“第一个value”,并将它赋值到了当前value上。
之后做了一个判断,判断当前value是否为空,为空就将当前value设置为“最后一个value”。
之后从当前value(即:第一个value)开始调用start方法,每调用完一个,就将当前value的下一个value赋值到当前value上。即:循环调用该管道内所有value的start方法。
我们再来看一下value的start方法。
value继承于生命周期接口,所以调用start就是调用其子类的startInternal()方法。
所有的子value都继承于ValueBase类,而startInternal方法就是定义在ValueBase类里,源码如下:
protected synchronized void startInternal() throws LifecycleException { this.setState(LifecycleState.STARTING); }
可以看出ValueBase中的startInternal方法只做了一件事,就是将当前状态设置为STARTING。
至此,第一条流程就完成了。
第二条流程为:Connector的Adapter将request和response传给Container的管道,从Engine的管道一路处理到Wrapper的管道,Wrapper再将response一路返回给Engine,然后Engine将response返回给Connector,Connector再返回给用户浏览器。
需要提前说明的是,四个自容器的管道中,每个管道都有多个value用来依次处理请求,但我在这主要分析“每个子容器管道的最后一个value”,分别是:StandardEngineValue、StandardHostValue、StandardContextValue、StandardWrapperValue。
StandardEngineValue:
final class StandardEngineValve extends ValveBase { private static final StringManager sm = StringManager.getManager("org.apache.catalina.core"); public StandardEngineValve() { super(true); } public final void invoke(Request request, Response response) throws IOException, ServletException { Host host = request.getHost(); if(host == null) { response.sendError(400, sm.getString("standardEngine.noHost", new Object[]{request.getServerName()})); } else { if(request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); } host.getPipeline().getFirst().invoke(request, response); } } public final void event(Request request, Response response, CometEvent event) throws IOException, ServletException { request.getHost().getPipeline().getFirst().event(request, response, event); } }
这里的invoke方法主要就做了一件事:
根据请求找到所对应的站点(Host),然后找到host的管道,调用其头部value的invoke方法,并把request和response传给他。
StandardHostValue:
其invoke源码为:
public final void invoke(Request request, Response response) throws IOException, ServletException { Context context = request.getContext(); if(context == null) { response.sendError(500, sm.getString("standardHost.noContext")); } else { if(request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } boolean asyncAtStart = request.isAsync(); boolean asyncDispatching = request.isAsyncDispatching(); try { context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if(!asyncAtStart && !context.fireRequestInitEvent(request)) { return; } try { if(asyncAtStart && !asyncDispatching) { if(!response.isErrorReportRequired()) { throw new IllegalStateException(sm.getString("standardHost.asyncStateError")); } } else { context.getPipeline().getFirst().invoke(request, response); } } catch (Throwable var10) { ExceptionUtils.handleThrowable(var10); if(response.isErrorReportRequired()) { this.container.getLogger().error("Exception Processing " + request.getRequestURI(), var10); } else { request.setAttribute("javax.servlet.error.exception", var10); this.throwable(request, response, var10); } } response.setSuspended(false); Throwable t = (Throwable)request.getAttribute("javax.servlet.error.exception"); if(!context.getState().isAvailable()) { return; } if(response.isErrorReportRequired()) { if(t != null) { this.throwable(request, response, t); } else { this.status(request, response); } } if(!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) { context.fireRequestDestroyEvent(request); } } finally { if(ACCESS_SESSION) { request.getSession(false); } context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); } } }
Host的用处再上上面已经讲过了,一个host代表一个站点(也叫一个虚拟主机),
其处理过程可以总结如下:
1、先根据request请求,选择一个context容器。如果不为空,则继续处理。
2、调用Context容器的bind()方法。此方法获取一条线程,然后让该线程处理Context。
3、获取context的管道,再调用管道中头value的invoke方法,将request和response传入其中。
StandardContextValue:
其invoke源码如下:
public final void invoke(Request request, Response response) throws IOException, ServletException { MessageBytes requestPathMB = request.getRequestPathMB(); if(!requestPathMB.startsWithIgnoreCase("/META-INF/", 0) && !requestPathMB.equalsIgnoreCase("/META-INF") && !requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0) && !requestPathMB.equalsIgnoreCase("/WEB-INF")) { Wrapper wrapper = request.getWrapper(); if(wrapper != null && !wrapper.isUnavailable()) { try { response.sendAcknowledgement(); } catch (IOException var6) { this.container.getLogger().error(sm.getString("standardContextValve.acknowledgeException"), var6); request.setAttribute("javax.servlet.error.exception", var6); response.sendError(500); return; } if(request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); } else { response.sendError(404); } } else { response.sendError(404); } }
其处理过程可以总结如下:
1、先获取request的请求路径,该路径不能直接访问WEB-INF或者META-INF目录下的资源。
2、再根据request,选择合适的Wrapper。
3、再在response中设置一个sendAcknowledgement(在TCP/IP协议中,如果接收方成功的接收到数据,那么会回复一个ACK数据。)
4、获取Wrapper的管道,再调用管道中头value的invoke方法,将request和response传入其中。
StandardWrapperValue:
每个Wrapper都封装着一个servlet,所以Wrapper和servlet有着很深的联系。
StandardWrapper里会加载他代表的servlet并创建实例(即调用它的init方法),但不会调用servlet的service方法。
StandardWrapperValue会调用sertvlet的service方法。
其具体执行顺序如下,
1、分配一个Servlet实例,因为StandardWrapper负责加载servlet,所以也是从Wrapper中获取servlet。
try { if(!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException var38) { this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), var38); long requestPathMB = wrapper.getAvailable(); if(requestPathMB > 0L && requestPathMB < 9223372036854775807L) { response.setDateHeader("Retry-After", requestPathMB); response.sendError(503, sm.getString("standardWrapper.isUnavailable", new Object[]{wrapper.getName()})); } else if(requestPathMB == 9223372036854775807L) { response.sendError(404, sm.getString("standardWrapper.notFound", new Object[]{wrapper.getName()})); } } catch (ServletException var39) { this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), StandardWrapper.getRootCause(var39)); throwable = var39; this.exception(request, response, var39); } catch (Throwable var40) { ExceptionUtils.handleThrowable(var40); this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), var40); throwable = var40; this.exception(request, response, var40); servlet = null; }
2、执行Servlet相关的所有过滤器:
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); try { if(servlet != null && filterChain != null) { if(context.getSwallowOutput()) { boolean var29 = false; try { var29 = true; SystemLogHandler.startCapture(); if(request.isAsyncDispatching()) { ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch(); var29 = false; } else if(comet1) { filterChain.doFilterEvent(request.getEvent()); var29 = false; } else { filterChain.doFilter(request.getRequest(), response.getResponse()); var29 = false; } } finally { if(var29) { String time = SystemLogHandler.stopCapture(); if(time != null && time.length() > 0) { context.getLogger().info(time); } } } String t2 = SystemLogHandler.stopCapture(); if(t2 != null && t2.length() > 0) { context.getLogger().info(t2); } } else if(request.isAsyncDispatching()) { ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch(); } else if(comet1) { filterChain.doFilterEvent(request.getEvent()); } else { filterChain.doFilter(request.getRequest(), response.getResponse()); } } }
ApplicationFilterChain可以看做是一个“过滤器链”,StandardWrapperValve中的invoke会先创建该类的实例,然后调用它的doFilter方法,即从该链中的第一个过滤器开始调用。如果执行到了最后一个过滤器,就开始调用Servlet的service方法。
3、关闭过滤器链
if(filterChain != null) { if(request.isComet()) { filterChain.reuse(); } else { filterChain.release(); } }
4、通知Wrapper,重新委派处理完毕的servlet。
try { if(servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable var31) { ExceptionUtils.handleThrowable(var31); this.container.getLogger().error(sm.getString("standardWrapper.deallocateException", new Object[]{wrapper.getName()}), var31); if(throwable == null) { throwable = var31; this.exception(request, response, var31); } }
至此Container第二条流程也完成了。
总结出来,简易流程图如下: