本文主要是介绍Tomcat分析一——Tomcat的顶层结构及启动过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.1 Tomcat的顶层结构
Tomcat最顶层容器叫Server,代表整个服务器,Server中至少有一个Service,用于提供服务。
Service主要包含两部分:
Connector:用于处理连接相关的事情,并提供Socket与request、response的转换
Container:用于封装和管理Servlet及具体处理request请求。
一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以有多个Connector。(因为一个服务可以有多个连接,如同时提供http和https连接,也可以提供相同协议不同端口的连接)
Tomcat中的Server由Catalina管理,Catalina是整个Tomcat的管理类,里面的三个方法load、start、stop分别管理整个服务器的生命周期,load方法用于根据Tomcat的 conf/server.xml 文件创建Server并调用Server的init方法进行初始化,start方法用于启动服务器,stop方法用于停止服务器,start和sto方法内部分别调用Server的start和stop方法,load内部调用了Server的init方法。
这三个方法都会按容器的结构逐层调用相应的方法,如Server的start方法中会调用所有Service中的start方法,Service中的start方法会调用所有包含的Connectors和Container的start方法,这样整个服务器就启动了。init和stop方法也一样,这就是Tomcat的生命周期的管理方式。
Catalina的await方法直接调用了Server的await方法,作用是进入一个循环,让主线程不会退出。
Tomcat的入口Bootstrap中,Bootstrap的作用类似CatalinaAdaptor,具体处理过程还是使用Catalina完成。好处是把启动的入口和具体的管理类分开,方便创建多种启动方式,每种方式只需要写一个相应的CatalinaAdaptor。
1.2 Bootstrap的启动过程
正常启动Tomcat调用Bootstrap的main方法,主要分为两部分:
1.2.1 首先新建Bootstrap,并执行init方法初始化
init方法初始化ClassLoader,并用ClassLoader创建Catalina实例,赋给catalinaDaemon变量,后面对命令的操作都使用catalinaDaemon具体执行。
1.2.2 处理main方法传入的命令,如果args参数为空,默认执行start
start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。都调用了Catalina的相应方法进行具体执行,是用反射来调用的。
public static void main(String[] args) {if (daemon == null) { // 先新建一个BootstrapBootstrap bootstrap = new Bootstrap();try {//初始化ClassLoader,并用ClassLoader创建Catalina实例,赋给catalinaDaemon变量bootstrap.init();} catch (Throwable var3) {handleThrowable(var3);var3.printStackTrace();return;}daemon = bootstrap;} else {Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}try {String command = "start";if (args.length > 0) {command = args[args.length - 1];}if (command.equals("startd")) {args[args.length - 1] = "start";daemon.load(args);daemon.start();} else if (command.equals("stopd")) {args[args.length - 1] = "stop";daemon.stop();} else if (command.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.");}} catch (Throwable var4) {Throwable t = var4;if (var4 instanceof InvocationTargetException && var4.getCause() != null) {t = var4.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}
}
start方法/setAwait方法/load方法——>根据反射调用catalina的start/setAwait/load方法
首先判断catalinaDaemon是否初始化,否则调用init方法进行初始化。然后使用Method进行反射调用Catalina的start方法。((Catalina)catalinaDaemon).start()。
Method是reflect包的类,代表一个具体的方法,可以使用其invoke方法执行代表的方法,第一个参数是Method方法所在的实体,第二个参数是可变参数,用于Method方法执行时需要的参数。
catalina.start()——>server.start()——>service.start()——>connector.start()/container.start()
public void start() throws Exception {if (this.catalinaDaemon == null) {this.init();}Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);method.invoke(this.catalinaDaemon, (Object[])null);
}public void setAwait(boolean await) throws Exception {Class<?>[] paramTypes = new Class[]{Boolean.TYPE};Object[] paramValues = new Object[]{await};Method method = this.catalinaDaemon.getClass().getMethod("setAwait",paramTypes);method.invoke(this.catalinaDaemon, paramValues);}
private void load(String[] arguments) throws Exception {String methodName = "load";Object[] param;Class[] paramTypes;if (arguments != null && arguments.length != 0) {paramTypes = new Class[]{arguments.getClass()};param = new Object[]{arguments};} else {paramTypes = null;param = null;}Method method = this.catalinaDaemon.getClass().getMethod(methodName,paramTypes);if (log.isDebugEnabled()) {log.debug("Calling startup class " + method);}method.invoke(this.catalinaDaemon, param);}
1.3 Catalina的启动过程
Catalina的启动主要是调用setAwait、load和start方法完成的。
setAwait 方法用于设置Server启动完成后是否进入等待状态的标志,true则进入;
load 方法用于加载配置文件,创建并初始化Server;
start 方法用于启动服务器。
1.3.1 setAwait(boolean)方法
setAwait方法设置await属性的值,await属性在start方法中 服务器启动后使用它判断是否进入等待状态。
public class Catalina {protected static final StringManager sm = StringManager.getManager("org.apache.catalina.startup");protected boolean await = false;protected String configFile = "conf/server.xml";public void setAwait(boolean b) {this.await = b;}
1.3.2 load 方法
load方法根据conf/server.xml创建Server对象,并赋值给server属性,解析操作使用Tomcat开源的Digester完成的,然后调用server的init方法
public void load() {long t1 = System.nanoTime();this.initDirs();this.initNaming();Digester digester = this.createStartDigester();InputSource inputSource = null;InputStream inputStream = null;File file = null;.....file = this.configFile();// conf/server.xmlinputStream = new FileInputStream(file);inputSource = new InputSource(file.toURI().toURL().toString());inputSource.setByteStream((InputStream)inputStream);digester.push(this);digester.parse(inputSource);......this.getServer().setCatalina(this);this.getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());this.getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());this.initStreams();try {this.getServer().init();} catch (LifecycleException var24) {}long t2 = System.nanoTime();if (log.isInfoEnabled()) {log.info("Initialization processed in " + (t2 - t1) / 1000000L + " ms");}}
1.3.3 start方法
Catalina的start方法主要调用了server的start方法启动服务器,并根据await属性判断是否让程序进入等待状态
-
先判断Server是否存在,不存在则调用load方法初始化Server
-
然后调用Server的start方法启动服务器
-
最后注册关闭钩子并根据await属性判断是否进入等待状态,由于之前设置为true,所以需要等待
-
进入等待状态会调用await和stop两个方法,await方法直接调用了Server的await方法,会执行一个while循环,这样程序就停在await方法,当await方法的while循环退出时,就会执行stop方法,从而关闭服务器。
server的await方法里,while循环根据volatile类型的stopAwait是否为true停止,默认false,一直循环
public void start() {if (this.getServer() == null) {this.load();}..... try {this.getServer().start();} catch (LifecycleException var7) {try {this.getServer().destroy();} return;}if (this.useShutdownHook) {if (this.shutdownHook == null) {this.shutdownHook = new Catalina.CatalinaShutdownHook();}Runtime.getRuntime().addShutdownHook(this.shutdownHook);LogManager logManager = LogManager.getLogManager();if (logManager instanceof ClassLoaderLogManager) {((ClassLoaderLogManager)logManager).setUseShutdownHook(false);}}if (this.await) {this.await();this.stop();}}
}
1.4 Server的启动过程
类继承和实现的关系图
Server接口的addService(Service)、removeService(Service)添加和删除Service、Server的init方法和start方法分别循环调用每个Service的init方法和start方法启动所有Service。
Server默认的实现是StandardServer,StandardServer又继承自LifecycleMBeanBase
LifecycleMBeanBase又继承自LifecycleBase
LifecycleBase中定义了init和start方法
LifecycleBase里的init方法和start方法又调用initInternal方法和startInternal方法,这两个方法都是模版方法,由子类具体实现,
public final synchronized void init() throws LifecycleException {if (!this.state.equals(LifecycleState.NEW)) {this.invalidTransition("before_init");}try {this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false);this.initInternal();this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false);} catch (Throwable var2) {ExceptionUtils.handleThrowable(var2);this.setStateInternal(LifecycleState.FAILED, (Object)null, false);}
}protected abstract void initInternal() throws LifecycleException;public final synchronized void start() throws LifecycleException {try {this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null,false);this.startInternal();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);}protected abstract void startInternal() throws LifecycleException;
所以调用StandardServer的init方法和start方法会执行StandardServer自己的initInternal方法和startInternal方法,里面又分别执行所有的services的init和start方法。
protected void initInternal() throws LifecycleException {super.initInternal();MBeanFactory factory = new MBeanFactory();factory.setContainer(this);this.onameMBeanFactory = this.register(factory, "type=MBeanFactory");this.globalNamingResources.init();for(int i = 0; i < this.services.length; ++i) {this.services[i].init();}
}
protected void startInternal() throws LifecycleException {this.fireLifecycleEvent("configure_start", (Object)null);this.setState(LifecycleState.STARTING);this.globalNamingResources.start();synchronized(this.servicesLock) {for(int i = 0; i < this.services.length; ++i) {this.services[i].start();}}
}
StandardServer的await方法
await方法处理流程,省略了一些处理异常、关闭Socket及对接收到数据处理的代码。
首先判断端口号port,然后根据port的值分为三种处理方法:
-
port为-2,会直接退出,不进行循环
-
port为-1,会进入while(!stopAwait)的循环,且内部没有berak跳出的语句,stopAwait标志只有调用了stop方法才会设置为true,所以port为-1时只有在外部调用stop方法才会退出循环
-
port为其他值,会进入一个while(!stopAwait)循环,同时会在port所在的端口启动一个ServerSocker监听关闭命令,接收到了则使用break跳出循环。
这里的端口port和关闭命令shutdown是在conf/server.xml文件中配置Server时设置的。默认设置如下
<!-- server.xml --> <Server port="8005" shutdown="SHUTDOWN">
这时在8005端口监听"SHUTDOWN"命令,接收到了就会关闭Tomcat,如果不想使用网络命令来关闭服务器可以将端口设置为-1。
await方法中从端口接受到数据后还会进行处理,如果接收到的数据中有ASCII码小于32的(ASCII中32以下的为控制符)则从小于32的字符截断并丢弃后面的数据。
1.5 Service的启动过程
Service默认的实现是StandardService,StandardService也继承自LifecycleMBeanBase,
StandardService的init方法和start方法最终会执行StandardServer自己的initInternal方法和startInternal方法
initInternal方法主要调用engine、mapperListener、executor和connector的init方法
mapperListener是Mapper的监听器,监听container容器的变化,
executors是用在connectors中管理线程的线程池
protected void initInternal() throws LifecycleException {super.initInternal();if (engine != null) {engine.init();}// Initialize any Executorsfor (Executor executor : findExecutors()) {if (executor instanceof JmxEnabled) {((JmxEnabled) executor).setDomain(getDomain());}executor.init();}// Initialize mapper listenermapperListener.init();// Initialize our defined Connectorssynchronized (connectorsLock) {for (Connector connector : connectors) {connector.init();}}
}
startInternal主要调用engine、mapperListener、executor和connector的start方法
protected void startInternal() throws LifecycleException {setState(LifecycleState.STARTING);// Start our defined Container firstif (engine != null) {synchronized (engine) {engine.start();}}synchronized (executors) {for (Executor executor: executors) {executor.start();}}mapperListener.start();// Start our defined Connectors secondsynchronized (connectorsLock) {for (Connector connector: connectors) {// If it has already failed, don't try and start itif (connector.getState() != LifecycleState.FAILED) {connector.start();}}}
}
现在整个Tomcat服务器就启动了,整个启动流程
这篇关于Tomcat分析一——Tomcat的顶层结构及启动过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!