Tomcat笔记:Tomcat的执行流程解析
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第二条流程也完成了。
总结出来,简易流程图如下:
本文转载自
原文作者:black-ant
原文链接:https://blog.csdn.net/zzg19950824/article/details/79433735
Tomcat笔记:Tomcat的执行流程解析的更多相关文章
- TIJ读书笔记02-控制执行流程
TIJ读书笔记02-控制执行流程 TIJ读书笔记02-控制执行流程 if-else 迭代 无条件分支 switch语句 所有条件语句都是以条件表达式的真假来决定执行路径,也就是通过布尔测试结果来决 ...
- Spark修炼之道(进阶篇)——Spark入门到精通:第九节 Spark SQL执行流程解析
1.总体执行流程 使用下列代码对SparkSQL流程进行分析.让大家明确LogicalPlan的几种状态,理解SparkSQL总体执行流程 // sc is an existing SparkCont ...
- Scrapy框架的执行流程解析
这里主要介绍七个大类Command->CrawlerProcess->Crawler->ExecutionEngine->sceduler另外还有两个类:Request和Htt ...
- java方法执行流程解析
Java程序运行时,必须经过编译和运行两个步骤.首先将后缀名为.java的源文件进行编译,最终生成后缀名为.class的字节码文件.然后Java虚拟机将编译好的字节码文件加载到内存(这个过程被称为类加 ...
- JavaWebServle执行流程解析
Servlet Servlet 1.定义: 注:servlet版本2.5 Servlet是Java的一个类.既然是一个类.那必然要遵循一定的规范.如下所示. a.必须继承 javax.servlet. ...
- c语言学习笔记 if语句执行流程和关系运算符
回想现实生活中,我们会遇到这样的情况,如果下雨了就带伞上班,如果没下雨就不带伞上班,这是很正常的逻辑.程序是解决生活中的问题的,那么自然在程序中也需要这样的判断,当满足某个条件的时候做一件事情,这种东 ...
- SpringMVC 学习笔记(十一) SpirngMVC执行流程
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYTY3NDc0NTA2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- 2.ibatis执行流程解析
以下仅为个人理解,如有问题,欢迎指正
- 【漏洞复现】Tomcat CVE-2017-12615 远程代码执行漏洞
漏洞描述 [漏洞预警]Tomcat CVE-2017-12615远程代码执行漏洞/CVE-2017-12616信息泄漏 https://www.secfree.com/article-395.html ...
随机推荐
- Visual C++实现局域网IP多播
//////////////////////////////////////////////////////////////////////////////////////////////////// ...
- 从点到面,给Button的属性动画
属性动画是API 11加进来的一个新特性,其实在现在来说也没什么新的了.属性动画可以对任意view的属性做动画,实现动画的原理就是在给定的时间内把属性从一个值变为另一个值.因此可以说属性动画什么都可以 ...
- (转)Log4Net 全方位跟踪程序运行
转自:http://www.cnblogs.com/qingyuan/archive/2011/05/13/2045616.html 前端日子自己写了一个简单的日志跟踪程序,现在目前正在做的一个项目中 ...
- 网络编程socket、udp
PS:主机字节顺序,个位在低字节上 计算机本身不能通讯,使通过在同一主机或者不同主机内的软件进行数据传输. 套接字socket:套接字socket可以参照文件指针来理解,文件指针是表示对文件打开进行某 ...
- android 发送url带中文出现乱码怎么解决
上传的时候参数中带中文的时候发送参数的时候就有可能出现乱码,这种情况怎么解决呢,就是设置url的格式为utf-8 httpRequest.setEntity(new UrlEncodedFormEnt ...
- 3D打印软件工具
切片与工艺规划(打印)软件: 3D打印中另外两个重要的步骤(切片和打印)中可以用到的一些开源,免费的软件和应用 Cura 开源, 免费 Python ...
- 加密算法比较3DES AES RSA ECC MD5 SHA1等
加 密算法通常分为对称性加密算法和非对称性加密算法,对于对称性加密算法,信息接收双方都需事先知道密匙和加解密算法且其密匙是相同的,之后便是对数据进行 加解密了.非对称算法与之不同,发送双方A,B事先均 ...
- Django 数据生命周期
- [require-js]向下滑动ajax加载的javascript实现
define(function(){ function ScrollMoreInfo($wraper , loadDataFunc , json_ids , perNum , tpl_info) { ...
- strom ui Topology 可视化视图各个指标含义说明
In the visualization, spout components are represented as blue, while bolts are colored between gree ...