Tomcat容器的Server模块有管理容器的启动和关闭、管理了容器内的服务组件Service、管理了全局JNDI资源的功能,对Tomcat容器的生命周期管理有重要意义。Tomcat的服务组件则是Tomcat的两个核心组件连接器和servlet容器之间的桥梁。本文会对Tomcat容器的服务器组件Server和服务组件Service进行介绍。
服务器组件Server我们知道Tomcat容器启动之后就可以一直保持服务,即使请求出现异常也不会退出,只有在收到特定的容器关闭命令时才会退出。Tomcat容器是怎么实现容器的启动?启动之后是如何保证容器一直保持运行?在收到容器关闭命令的时候怎么优雅关闭的呢?这就是Tomcat容器中的Server的功能了。
每个Tomcat容器都会唯一包含一个Server组件,对应于Tomcat安装文件夹下面的server.xml。下面为Tomcat10安装包中conf/server.xml的默认配置。分析xml可知,server节点有 port和shutdown属性,包含Listener、GlobalNamintResources和Service三部分子节点。下文我们会分别对这些内容进行介绍。
<?xml version="1.0" encoding="UTF-8"?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Engine defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server> Server的启动/停止通过上文可以知道,Server组件重要的就是控制Tomcat容器的启动/停止,然而启动停止并不是简单的启动JVM关闭JVM就可以了,Tomcat容器启动/停止是还必须调用容器内所有组件的生命周期方法,启动时需要所有的组件进行初始化,结束时需要所有的组件进行销毁和资源释放。
JVM的启动/停止JVM的启动比较简单,我们在运行tomcat启动脚本的时候,会启动tomcat的Jar文件,从而启动JVM。
关于JVM的退出则稍微复杂一些,JVM退出的方式分为以下三种类型:
正常关闭:当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭(发送SIGINT,SIGTERM信号等)
异常关闭:运行中遇到RuntimeException异常等。
强制关闭:通过调用Runtime.halt方法或者是在操作系统中直接kill(发送SIGKILL信号)掉JVM进程
对于正常关闭和异常关闭,JVM都有机会执行关闭的Hook方法,对于强制关闭则不一定会执行关闭时的hook方法。所以我们在日常使用中应该尽量避免使用kill -9等方法退出JVM。
JVM注册Shutdown Hook的方法如下所示:
Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { // } });Tomcat容器启动的时候会通过Runtime.getRuntime().addShutdownHook(Runnable run)方法向JVM注册关闭回调方法CatalinaShutdownHook,从而实现容器的优雅关闭。
Tomcat关闭接口我们上面讲了JVM退出的情况下Tomcat怎么实现优雅的关闭,Tomcat也可以主动关闭程序,我们在配置server.xml文件的时候,会指定server的port和shutdown指令,在需要关闭Tomcat容器的时候,我们只需要向指定端口发送关闭指令,Tomcat就会主动退出服务。
<?xml version="1.0" encoding="UTF-8"?> <Server port="8005" shutdown="SHUTDOWN"> </Server> 生命周期的控制Tomcat中需要实现声明周期管理的组件都会实现Lifecycle接口。通过上文我们知道Tomcat的启动/停止是由Server控制的,那么Server是如何通知容器内的其它组件(如container、connector)启动/停止相关事件的呢?我们先看看Tomcat的结构图,我们可以看到Tomcat容器的组件之间是一层层包含关系,一个Server包含多个Service,一个Service包含多个Container等等。