Tommcat源码学习(三)--Tomcat_7.0.70停止服务过程分析
2016-07-05 00:00
435 查看
摘要: Tommcat源码学习(三)--Tomcat_7.0.70停止服务过程分析
Tomcat关闭命令(Linux下,大部分生产环境都是部署在Linux系统下):
执行这个命令之后,tomcat会为我们做了哪些操作呢?下面就来简单分析下。
shutdown.sh代码清单如下:
从上面的代码可以看出来,这里和启动文件是一样的,也有主要的两个变量:
从最后一行代码可以知道,执行catalina.sh,并传递参数:stop
catalina.sh 与之相关的代码清单如下:
最终使用java命令执行了org.apache.catalina.startup.Bootstrap类中的main方法,参数是stop。
当传递参数stop的时候,command等于stop,此时main方法的执行步骤如下:
初始化Bootstrap(启动的时候已经介绍过就不在介绍)
停止服务
通过调用Bootstrap的stopServer方法停止Tomcat,其实质是用反射调用catalinaDaemon(类型是Catalina)的stopServer方法。
Catalina的stopServer方法的执行步骤如下:
代码清单:
创建Digester解析server.xml文件(此处只解析标签),以构造出Server容器(此时Server容器的子容器没有被实例化);
从实例化的Server容器获取Server的socket监听端口和地址,然后创建Socket对象连接启动Tomcat时创建的ServerSocket,最后向ServerSocket发送SHUTDOWN命令。根据
内容,ServerSocket循环等待接收到SHUTDOWN命令后,最终调用stop方法停止Tomcat。
最后,我们看看Catalina的stop方法的实现,其执行步骤如下:
将启动过程中添加的关闭钩子移除。Tomcat启动过程辛辛苦苦添加的关闭钩子为什么又要去掉呢?因为关闭钩子是为了在JVM异常退出后,进行资源的回收工作。主动停止Tomcat时调用的stop方法里已经包含了资源回收的内容,所以不再需要这个钩子了。
停止Server容器。有关容器的停止内容,请阅读后续文章。
代码清单:
总结:
通过对Tomcat源码的分析我们了解到Tomcat的启动和停止都离不开org.apache.catalina.startup.Bootstrap。
当停止Tomcat时,已经启动的Tomcat作为socket服务端,停止脚本启动的Bootstrap进程作为socket客户端向服务端发送shutdown命令
,两个进程通过共享server.xml里Server标签的端口以及地址信息打通了socket的通信。
Tomcat关闭命令(Linux下,大部分生产环境都是部署在Linux系统下):
sh shutdown.sh
执行这个命令之后,tomcat会为我们做了哪些操作呢?下面就来简单分析下。
shutdown.sh代码清单如下:
# Better OS/400 detection: see Bugzilla 31132 os400=false case "`uname`" in OS400*) os400=true;; esac # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` EXECUTABLE=catalina.sh # Check that target executable exists if $os400; then # -x will Only work on the os400 if the files are: # 1. owned by the user # 2. owned by the PRIMARY group of the user # this will not work if the user belongs in secondary groups eval else if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then echo "Cannot find $PRGDIR/$EXECUTABLE" echo "The file is absent or does not have execute permission" echo "This file is needed to run this program" exit 1 fi fi exec "$PRGDIR"/"$EXECUTABLE" stop "$@"
从上面的代码可以看出来,这里和启动文件是一样的,也有主要的两个变量:
PRGDIR:当前shell脚本所在的路径; EXECUTABLE:脚本catalina.sh
从最后一行代码可以知道,执行catalina.sh,并传递参数:stop
catalina.sh 与之相关的代码清单如下:
elif [ "$1" = "stop" ] ; then shift SLEEP=5 if [ ! -z "$1" ]; then echo $1 | grep "[^0-9]" >/dev/null 2>&1 if [ $? -gt 0 ]; then SLEEP=$1 shift fi fi FORCE=0 if [ "$1" = "-force" ]; then shift FORCE=1 fi if [ ! -z "$CATALINA_PID" ]; then if [ -f "$CATALINA_PID" ]; then if [ -s "$CATALINA_PID" ]; then kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 if [ $? -gt 0 ]; then echo "PID file found but no matching process was found. Stop aborted." exit 1 fi else echo "PID file is empty and has been ignored." fi else echo "\$CATALINA_PID was set but the specified file does not exist. Is Tomcat running? Stop aborted." exit 1 fi fi eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \ -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ -Dcatalina.base="\"$CATALINA_BASE\"" \ -Dcatalina.home="\"$CATALINA_HOME\"" \ -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ org.apache.catalina.startup.Bootstrap "$@" stop
最终使用java命令执行了org.apache.catalina.startup.Bootstrap类中的main方法,参数是stop。
public static void main(String args[]) { if (daemon == null) { // Don't set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { // When running as a service the call to stop will be on a new // thread so make sure the correct class loader is used to prevent // a range of class not found exceptions. 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 t) { // Unwrap the Exception for clearer error reporting if (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1); } }
当传递参数stop的时候,command等于stop,此时main方法的执行步骤如下:
初始化Bootstrap(启动的时候已经介绍过就不在介绍)
停止服务
通过调用Bootstrap的stopServer方法停止Tomcat,其实质是用反射调用catalinaDaemon(类型是Catalina)的stopServer方法。
/** * Stop the standalone server. */ public void stopServer(String[] arguments) throws Exception { Object param[]; Class<?> paramTypes[]; if (arguments==null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod("stopServer", paramTypes);//反射调用catalinaDaemon(类型是Catalina)的stopServer方法 method.invoke(catalinaDaemon, param); }
Catalina的stopServer方法的执行步骤如下:
代码清单:
public void stopServer() { stopServer(null); } public void stopServer(String[] arguments) { if (arguments != null) { arguments(arguments); } Server s = getServer(); if( s == null ) {//服务不存在 // Create and execute our Digester Digester digester = createStopDigester();//Digester解析server.xml文件,以构造出Server容器 File file = configFile(); FileInputStream fis = null; try { InputSource is = new InputSource(file.toURI().toURL().toString()); fis = new FileInputStream(file); is.setByteStream(fis); digester.push(this); digester.parse(is); } catch (Exception e) { log.error("Catalina.stop: ", e); System.exit(1); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { // Ignore } } } } else { // Server object already present. Must be running as a service try { s.stop(); } catch (LifecycleException e) { log.error("Catalina.stop: ", e); } return; } // Stop the existing server s = getServer(); if (s.getPort()>0) { Socket socket = null; OutputStream stream = null; try { socket = new Socket(s.getAddress(), s.getPort());//创建Socket对象连接启动Tomcat时创建的ServerSocket stream = socket.getOutputStream(); String shutdown = s.getShutdown(); for (int i = 0; i < shutdown.length(); i++) { stream.write(shutdown.charAt(i));//发送shutdown命令 } stream.flush(); } catch (ConnectException ce) { log.error(sm.getString("catalina.stopServer.connectException", s.getAddress(), String.valueOf(s.getPort()))); log.error("Catalina.stop: ", ce); System.exit(1); } catch (IOException e) { log.error("Catalina.stop: ", e); System.exit(1); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // Ignore } } if (socket != null) { try { socket.close(); } catch (IOException e) { // Ignore } } } } else { log.error(sm.getString("catalina.stopServer")); System.exit(1); } }
创建Digester解析server.xml文件(此处只解析标签),以构造出Server容器(此时Server容器的子容器没有被实例化);
从实例化的Server容器获取Server的socket监听端口和地址,然后创建Socket对象连接启动Tomcat时创建的ServerSocket,最后向ServerSocket发送SHUTDOWN命令。根据
@Override public void await() { // Negative values - don't wait on port - tomcat is embedded or we just don't like ports if( port == -2 ) { // undocumented yet - for embedding apps that are around, alive. return; } if( port==-1 ) { try { awaitThread = Thread.currentThread(); while(!stopAwait) { try { Thread.sleep( 10000 ); } catch( InterruptedException ex ) { // continue and check the flag } } } finally { awaitThread = null; } return; } // Set up a server socket to wait on try { awaitSocket = new ServerSocket(port, 1, InetAddress.getByName(address));//创建socket连接的服务端对象ServerSocket } catch (IOException e) { log.error("StandardServer.await: create[" + address + ":" + port + "]: ", e); return; } try { awaitThread = Thread.currentThread(); // Loop waiting for a connection and a valid command while (!stopAwait) { ServerSocket serverSocket = awaitSocket; if (serverSocket == null) { break; } // Wait for the next connection Socket socket = null; StringBuilder command = new StringBuilder();//创建一个对象循环接收socket中的字符 try { InputStream stream; long acceptStartTime = System.currentTimeMillis(); try { socket = serverSocket.accept(); socket.setSoTimeout(10 * 1000); // Ten seconds stream = socket.getInputStream(); } catch (SocketTimeoutException ste) { // This should never happen but bug 56684 suggests that // it does. log.warn(sm.getString("standardServer.accept.timeout", Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste); continue; } catch (AccessControlException ace) { log.warn("StandardServer.accept security exception: " + ace.getMessage(), ace); continue; } catch (IOException e) { if (stopAwait) { // Wait was aborted with socket.close() break; } log.error("StandardServer.await: accept: ", e); break; } // Read a set of characters from the socket int expected = 1024; // Cut off to avoid DoS attack while (expected < shutdown.length()) { if (random == null) random = new Random(); expected += (random.nextInt() % 1024); } while (expected > 0) { int ch = -1; try { ch = stream.read(); } catch (IOException e) { log.warn("StandardServer.await: read: ", e); ch = -1; } if (ch < 32) // Control character or EOF terminates loop break; command.append((char) ch); expected--; } } finally { // Close the socket now that we are done with it try { if (socket != null) { socket.close(); } } catch (IOException e) { // Ignore } } // Match against our command string boolean match = command.toString().equals(shutdown); if (match) { //如果接收到的命令与SHUTDOWN匹配(由于使用了equals,所以shutdown命令必须是大写的),那么退出循环等待 log.info(sm.getString("standardServer.shutdownViaPort")); break; } else log.warn("StandardServer.await: Invalid command '" + command.toString() + "' received"); } } finally { ServerSocket serverSocket = awaitSocket; awaitThread = null; awaitSocket = null; // Close the server socket and return if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { // Ignore } } } }
内容,ServerSocket循环等待接收到SHUTDOWN命令后,最终调用stop方法停止Tomcat。
最后,我们看看Catalina的stop方法的实现,其执行步骤如下:
将启动过程中添加的关闭钩子移除。Tomcat启动过程辛辛苦苦添加的关闭钩子为什么又要去掉呢?因为关闭钩子是为了在JVM异常退出后,进行资源的回收工作。主动停止Tomcat时调用的stop方法里已经包含了资源回收的内容,所以不再需要这个钩子了。
停止Server容器。有关容器的停止内容,请阅读后续文章。
代码清单:
/** * Stop an existing server instance.**** */ public void stop() { try { // Remove the ShutdownHook first so that server.stop() // doesn't get invoked twice if (useShutdownHook) { Runtime.getRuntime().removeShutdownHook(shutdownHook);//将启动过程中添加的关闭钩子移除 // If JULI is being used, re-enable JULI's shutdown to ensure // log messages are not lost LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( true); } } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } // Shut down the server try { Server s = getServer(); LifecycleState state = s.getState(); if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 && LifecycleState.DESTROYED.compareTo(state) >= 0) { // Nothing to do. stop() was already called } else { s.stop();//停止Server容器。 s.destroy(); } } catch (LifecycleException e) { log.error("Catalina.stop", e); } }
总结:
通过对Tomcat源码的分析我们了解到Tomcat的启动和停止都离不开org.apache.catalina.startup.Bootstrap。
当停止Tomcat时,已经启动的Tomcat作为socket服务端,停止脚本启动的Bootstrap进程作为socket客户端向服务端发送shutdown命令
,两个进程通过共享server.xml里Server标签的端口以及地址信息打通了socket的通信。
相关文章推荐
- linux下查看tomcat和jdk版本号、重启tomcat、查看tomcat日志的命令
- IDEA上搭建jeesite框架
- Tomcat环境配置
- Eclipse下tomcat部署web项目lib包没有部署到web-inf/lib下
- tomcat 部署浏览器无法显示页面
- java.lang.IllefalStateException(tomcat热部署问题)
- 【Java】eclipse如何导出为war文件,热部署到tomcat运行总结
- 关于android studio模拟器无法直接通过10.0.2.2直接访问tomcat服务器的问题
- JavaWeb在tomcat下的几种发布和访问方式
- 我自己配置的生产环境的 centos 7 + nginx +tomcat + mysql5.6 + mmm
- IntelliJ IDEA创建web项目并且部署tomcat
- apache+tomcat+jk session共享配置
- tomcat .do发布是虚拟目录无法访问
- eclipse引入tomcat
- Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modules
- linux 下的 tomcat
- idea配置tomcat
- Mac下修改tomcat指定jdk版本
- Mac下无权限执行tomcat中.sh命令
- Apache和Tomcat的关系