Tomcat的顶层结构及启动过程
1.Tomcat的顶层结构
Tomcat中最顶层的容器叫Server,代表整个服务器。
Server中包含至少一个Service,用于具体提供服务
Service主要包含两部分:Connector和Container
Connector用于处理连接相关的事情,并提供Socket与request、response的转换
Container用于封装和管理Servlet,以及具体处理request请求。
一个Tomcat中只有一个Server,一个Server可以有多个Service,一个Service只有一个Container,但可以有多个Connector(因为一个服务可以有多个连接,如同时提供http和https连接,也可以提供相同协议不同端口的连接)
Tomcat里的Server由org.apache.catalina.startup.Catalina来管理,Catalina是整个Tomcat的管理类,它里面的三个方法load、start、stop分别用来管理整个服务器的生命周期
load方法用于根据conf/server.xml文件创建Server并调用Server的init方法进行初始化
start方法用于启动服务器
stop方法用于停止服务器
start和stop方法在内部分别调用了Server的start和stop方法,load方法内部调用了Server的init方法,这三个方法会按容器的结构逐层调用相应的方法,比如Server的start方法中会调用所有的Service中的start方法,Service中的start方法又会调用所有包含的Container和Connectors的start方法,这样整个服务器就启动了,init和stop方法也一样
这就是Tomcat生命周期的管理方式
Catalina中的await方法直接调用了Server的await方法,这个方法的作用是进入一个循环,让主线程不会退出
不过Tomcat的入口main方法并不在Catalina类里,而是在org.apache.catalina.startup.Bootstrap中,Bootstrap的作用类似一个CatalinaAdaptor,
具体处理过程还是使用Catalina来完成的,这么做的好处是可以把启动的入口和具体的管理类分开,从而可以很方便地创建出多种启动方式,每种启动方式只需要写一个相应的CatalinaAdaptor就可以了。
2.Bootstrap的启动过程
Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用的Bootstrap的main方法
//org.apache.catalina.startip.Bootstrap
public static void main(String args[]){
//先新建一个Bootstrap
if(daemon == null){
Bootstrap bootstrap = new Bootstrap();
//初始化了ClassLoader,并用ClassLoader创建了Catalina实例,赋给catalinaDaemon变量
bootstrap.init();
daemon - bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
String command = "start";
if(arg.length > 0){
command = args[args.length - 1];
}
if(command.equals("startd")){
args[arg.length-1] = "start";
daemon.load(args);
daemon.start();
} else if (commond.equals("stopd")) {
args[args.length-1] = "stop";
daemon.stop();
} else if (commond.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if(null==daemon.getServer()){
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \""+command + "\" does not exist.");
}
}
可以看到时这里的main非常简单,只有两部分内容:首先新建了Bootstrap,并执行init方法初始化;然后处理mian方法传入的命令, 如果args参数为空,默认执行start
在init方法里初始化了ClassLoader,并用ClassLoader创建了Catalina实例,然后赋给catalinaDaemon变量,后面对命令的操作都要用它来具体执行。
对start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。这三个方法内部都使用反射调用了Catalina的相应方法进行具体执行
3.Catalina的启动过程
Catalina的启动主要是调用setAwait、load和start方法来完成的。setAwait方法用于设置Server启动完成后是否进入等等状态的标志, 如果为true则进入,否则不进入;load方法用于加载配置文件,创建并初始化Server;start方法用于启动服务器
4.Server的启动过程
Server接口中提供addService(Service service)、removeService(Service service)来添加和删除Service,Server的init方法和start方法分别循环调用了每个Service的init方法和start方法来启动所有Service。
Server的默认实现是org.apache.catalina.core.StandardServer, StandardServer继承LifecycleMBeanBase,LifecycleMBeanBase继承LifecycleBase,init和start方法就定义在LifecycleBase中,LifecycleBase里的init方法和start方法又调用initInternal方法和startInternal方法
这两个方法都是模板方法,由子类具体实现,所以调用StandardServer的init和start方法时会执行
StandardServer自己的initInternal和startInternal方法,这就是Tomcat生命周期的管理方式
//org.apache.catalina.core.StandardServer
protected void startInternal(){
......
synchronized(servicesLock){
for(int i=0;i<services.length;i++){
services[i].start();
}
}
}
protected void initInternal(){
......
for(int i=0;i<services.length;i++){
services[i].init();
}
}
StandadServer中还实现了await方法,Catalina中就是调用它让服务器进入等待状态的
处理的大概逻辑是首先判断端口号port,然后根据port的值分为三种处理方法
port为-2,则会直接退出,不进入循环
prot为-1,则会进入一个while(!stopAwait)的循环,并且在内部没有break跳出的语句,stopAwait标志只有调用了stop方法都会设置为true,所以port为-1时只有在外部调用stop才会退出循环
port为其他,则也会进入一个while(!stopAwait)的循环,不过同时会在port所在端口启动一个ServerSocket来监听关闭命令,如果接收到了则会使用break跳出循环
<!-- server.xml -->
<Server port = "8005" shutdown="SHUTDOWN">
5.Service的启动过程
Service的默认实现是org.apache.catalina.core.StandardService,StandardService也继承处LifecycleMBeanBase类,所以init和start方法最终也会调用initInternal和startInternal方法,这两个方法主要调用container、executors、mapperListener、connectors的init和start方法。
mapperListener是Mapper的监听器,可以监听container容器的变化,executors是用在connectors中管理线程的线程池,在server.xml配置文件中有用法
整个流程
Bootstrap - Catalina - StandardServer - StandardService - Container - MapperUstener - Executor - Connetor
init()方法一层调用一层返回之后start()方法再一层调用一层