Tomcat关闭过程

观察org.apache.catalina.startup.Catalina.java中的start()方法,我们发现Serever启动后,还要执行如下代码:

    /**
     * Await and shutdown.
     */
    public void await() {

        getServer().await();

    }

查询await()方法的实现,将跟到org.apache.catalina.core.StandServer中await()方法。

    try {
        awaitThread = Thread.currentThread();

        // Loop waiting for a connection and a valid command
        while (!stopAwait) {
            // ServerSocket[addr=localhost/127.0.0.1,localport=8005]
            ServerSocket serverSocket = awaitSocket;
            if (serverSocket == null) {
                break;
            }

            // Wait for the next connection
            Socket socket = null;
            StringBuilder command = new StringBuilder();
            try {
                InputStream stream;
                try {
                    socket = serverSocket.accept();
                    socket.setSoTimeout(10 * 1000);  // Ten seconds
                    stream = socket.getInputStream();
                } 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) {
                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
            }
        }
    }

根据该方法上的注释可以明白:该方法将一直等待,直到接收到一个合适的shutdown命令才会返回。这样会使主线程一直存活——监听http连接的线程池是守护线程。
该方法在默认地址的默认端口上(ServerSocket[addr=localhost/127.0.0.1,localport=8005])监听Socket连接,当发现监听的连接输入流中的内容与默认配置的值匹配(该值默认为字符串SHUTDOWN)则跳出循环,该方法返回。否则,该方法会一直循环执行下去。
默认端口的配置见Server.xml:<Server port="8005" shutdown="SHUTDOWN">

如下,当我们敲击”localhost:8005/SHUTDOWN”的url时,就会跳出循环。
执行shutdown

再次观察如下代码:

    // Match against our command string
    boolean match = command.toString().equals(shutdown);
    if (match) {
        log.info(sm.getString("standardServer.shutdownViaPort"));
        break;
    } else
        log.warn("StandardServer.await: Invalid command '"
                + command.toString() + "' received");

从输入流中读出的int转换为字符串后是”GET /SHUTDOWN HTTP/1.1”,而shutdown是默认值”SHUTDOWN”,最终无法跳出while循环,控制台也报出了超时的错误。

十二月 01, 2016 11:31:06 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:12 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command '' received
十二月 01, 2016 11:31:24 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:29 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:30 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:40 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: read: 
java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at org.apache.catalina.core.StandardServer.await(StandardServer.java:478)
    at org.apache.catalina.startup.Catalina.await(Catalina.java:727)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:673)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:450)

十二月 01, 2016 11:31:40 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command '' received

那如何通过在线的方式关闭服务器呢?
最简单的方法就是更改8005端口号的默认值为”GET /SHUTDOWN HTTP/1.1”,并重启服务,再次输入”localhost:8005/SHUTDOWN”,即可关闭Tomcat服务器。
成功跳出监听的循环

最后我们再来看org.apache.catalina.startup.Catalina类中stop方法,

    /**
     * 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();
                s.destroy();
            }
        } catch (LifecycleException e) {
            log.error("Catalina.stop", e);
        }

    }

移除完关闭钩子后,守护线程
1.调用stop()方法,和启动过程类似,会先后调用StandardServer和StandardService类的stopInternal()方法、加锁暂停HTTP Connector和AJP Connector的连接、加锁停止StandardEngine、再加锁停止HTTP Connector和AJP Connector的连接、调用NamingResources的stopInternal()方法等。
2.调用destroy()方法,会先后调用StandardServer和StandardService类的destroyInternal()方法、加锁销毁Connector、加锁销毁StandardEngine、销毁NamingResources等。
看如下代码:

    /**
     * Sub-classes wishing to perform additional clean-up should override this
     * method, ensuring that super.destroyInternal() is the last call in the
     * overriding method.
     */
    @Override
    protected void destroyInternal() throws LifecycleException {
        // oname -> Catalina:type=Service
        unregister(oname);
    }

各组件的destroy()方法执行完后要调用父类org.apache.catalina.util.LifecycleMBeanBase中的destroyInternal(),这意味着生命周期的结束!

若通过执行shutdown.bat或shutdown.sh脚本,最终会执行org.apache.catalina.startup.Bootstrap类的main方法,并传入参数”stop”,接着将调用org.apache.catalina.startup.Catalina类中的stopServer()方法,步骤和上述部分相似,这里就不再赘述。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值