Принцип отключения сервера Tomcat 7

Java задняя часть исходный код Tomcat

предыдущийнесколько статейГоворя о процессе запуска Tomcat, после его запуска в конфигурации по умолчанию вы увидите, что на самом деле в фоновом режиме работает 6 потоков. То есть 1 пользовательский поток, а остальные 5 — это потоки демона (Daemon Thread на рисунке ниже).

Если вы не знакомы с понятием потока демона, повторите его здесь:

Так называемый поток демона относится к потоку, который предоставляет общие услуги в фоновом режиме во время работы программы, например, поток сборки мусора. Этот поток не является составной частью программы.Когда все потоки, не являющиеся демонами, заканчиваются, программа завершается, и все потоки демонов в процессе уничтожаются. И наоборот, программа не завершится, пока все еще выполняются какие-либо потоки, не являющиеся демонами.

Между пользовательскими потоками и потоками демона почти нет разницы, единственное отличие состоит в выходе виртуальной машины: если все пользовательские потоки завершили работу, существуют только потоки демона, и виртуальная машина завершится. Поскольку опекуна нет, потоку демона нечего делать, и нет необходимости продолжать выполнение программы. Преобразование потока в поток демона можно выполнить, вызвав метод setDaemon(true) объекта Thread.

Выключение Tomcat использует этот принцип, то есть пока закрывается единственный пользовательский поток, закрывается все приложение.

Чтобы изучить, как закрывается этот пользовательский поток, мы должны сначала начать с того места, где этот поток генерируется. При анализе запуска Tomcat ранее мы начали сorg.apache.catalina.startup.BootstrapВ качестве входа используется метод main класса, а строки с 453 по 456 класса — это код, который Tomcat выполнит при запуске:

Методы daemon.load и daemon.start были проанализированы в предыдущей статье, обратите внимание здесьdaemon.setAwait(true);Это предложение, его роль состоит в том, чтобы вызвать через размышлениеorg.apache.catalina.startup.CatalinaНаконец, метод setAwait(true) класса устанавливает для переменной экземпляра await класса Catalina значение true .

Код метода Setawait для класса Catalina:

    /**
     * Set flag.
     */
    public void setAwait(boolean await)
        throws Exception {

        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Boolean.TYPE;
        Object paramValues[] = new Object[1];
        paramValues[0] = Boolean.valueOf(await);
        Method method =
            catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
        method.invoke(catalinaDaemon, paramValues);

    }

Как и в предыдущем анализе, он будет вызван для запуска Tomcat.org.apache.catalina.startup.CatalinaСтартовый метод класса, посмотрите на код этого метода:

     
     1	    /**
     2	     * Start a new server instance.
     3	     */
     4	    public void start() {
     5	
     6	        if (getServer() == null) {
     7	            load();
     8	        }
     9	
    10	        if (getServer() == null) {
    11	            log.fatal("Cannot start server. Server instance is not configured.");
    12	            return;
    13	        }
    14	
    15	        long t1 = System.nanoTime();
    16	
    17	        // Start the new server
    18	        try {
    19	            getServer().start();
    20	        } catch (LifecycleException e) {
    21	            log.fatal(sm.getString("catalina.serverStartFail"), e);
    22	            try {
    23	                getServer().destroy();
    24	            } catch (LifecycleException e1) {
    25	                log.debug("destroy() failed for failed Server ", e1);
    26	            }
    27	            return;
    28	        }
    29	
    30	        long t2 = System.nanoTime();
    31	        if(log.isInfoEnabled()) {
    32	            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
    33	        }
    34	
    35	        // Register shutdown hook
    36	        if (useShutdownHook) {
    37	            if (shutdownHook == null) {
    38	                shutdownHook = new CatalinaShutdownHook();
    39	            }
    40	            Runtime.getRuntime().addShutdownHook(shutdownHook);
    41	
    42	            // If JULI is being used, disable JULI's shutdown hook since
    43	            // shutdown hooks run in parallel and log messages may be lost
    44	            // if JULI's hook completes before the CatalinaShutdownHook()
    45	            LogManager logManager = LogManager.getLogManager();
    46	            if (logManager instanceof ClassLoaderLogManager) {
    47	                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
    48	                        false);
    49	            }
    50	        }
    51	
    52	        if (await) {
    53	            await();
    54	            stop();
    55	        }
    56	    }

Предыдущий анализ показал, что с помощью вызова метода getServer().start() в строке 19 Tomcat шаг за шагом запускает все компоненты, настроенные в файле конфигурации. Следующий код не анализируется, обратите внимание на последние строки с 52 по 55. Там указано, что переменная экземпляра await класса Catalina установлена ​​в true, поэтому здесь будет выполняться метод await класса Catalina:

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

Этот метод представляет собой всего одно предложение, что означает вызовorg.apache.catalina.core.StandardServerМетод ожидания класса:


     1	    /**
     2	     * Wait until a proper shutdown command is received, then return.
     3	     * This keeps the main thread alive - the thread pool listening for http 
     4	     * connections is daemon threads.
     5	     */
     6	    @Override
     7	    public void await() {
     8	        // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
     9	        if( port == -2 ) {
    10	            // undocumented yet - for embedding apps that are around, alive.
    11	            return;
    12	        }
    13	        if( port==-1 ) {
    14	            try {
    15	                awaitThread = Thread.currentThread();
    16	                while(!stopAwait) {
    17	                    try {
    18	                        Thread.sleep( 10000 );
    19	                    } catch( InterruptedException ex ) {
    20	                        // continue and check the flag
    21	                    }
    22	                }
    23	            } finally {
    24	                awaitThread = null;
    25	            }
    26	            return;
    27	        }
    28	
    29	        // Set up a server socket to wait on
    30	        try {
    31	            awaitSocket = new ServerSocket(port, 1,
    32	                    InetAddress.getByName(address));
    33	        } catch (IOException e) {
    34	            log.error("StandardServer.await: create[" + address
    35	                               + ":" + port
    36	                               + "]: ", e);
    37	            return;
    38	        }
    39	
    40	        try {
    41	            awaitThread = Thread.currentThread();
    42	
    43	            // Loop waiting for a connection and a valid command
    44	            while (!stopAwait) {
    45	                ServerSocket serverSocket = awaitSocket;
    46	                if (serverSocket == null) {
    47	                    break;
    48	                }
    49	    
    50	                // Wait for the next connection
    51	                Socket socket = null;
    52	                StringBuilder command = new StringBuilder();
    53	                try {
    54	                    InputStream stream;
    55	                    try {
    56	                        socket = serverSocket.accept();
    57	                        socket.setSoTimeout(10 * 1000);  // Ten seconds
    58	                        stream = socket.getInputStream();
    59	                    } catch (AccessControlException ace) {
    60	                        log.warn("StandardServer.accept security exception: "
    61	                                + ace.getMessage(), ace);
    62	                        continue;
    63	                    } catch (IOException e) {
    64	                        if (stopAwait) {
    65	                            // Wait was aborted with socket.close()
    66	                            break;
    67	                        }
    68	                        log.error("StandardServer.await: accept: ", e);
    69	                        break;
    70	                    }
    71	
    72	                    // Read a set of characters from the socket
    73	                    int expected = 1024; // Cut off to avoid DoS attack
    74	                    while (expected < shutdown.length()) {
    75	                        if (random == null)
    76	                            random = new Random();
    77	                        expected += (random.nextInt() % 1024);
    78	                    }
    79	                    while (expected > 0) {
    80	                        int ch = -1;
    81	                        try {
    82	                            ch = stream.read();
    83	                        } catch (IOException e) {
    84	                            log.warn("StandardServer.await: read: ", e);
    85	                            ch = -1;
    86	                        }
    87	                        if (ch < 32)  // Control character or EOF terminates loop
    88	                            break;
    89	                        command.append((char) ch);
    90	                        expected--;
    91	                    }
    92	                } finally {
    93	                    // Close the socket now that we are done with it
    94	                    try {
    95	                        if (socket != null) {
    96	                            socket.close();
    97	                        }
    98	                    } catch (IOException e) {
    99	                        // Ignore
   100	                    }
   101	                }
   102	
   103	                // Match against our command string
   104	                boolean match = command.toString().equals(shutdown);
   105	                if (match) {
   106	                    log.info(sm.getString("standardServer.shutdownViaPort"));
   107	                    break;
   108	                } else
   109	                    log.warn("StandardServer.await: Invalid command '"
   110	                            + command.toString() + "' received");
   111	            }
   112	        } finally {
   113	            ServerSocket serverSocket = awaitSocket;
   114	            awaitThread = null;
   115	            awaitSocket = null;
   116	
   117	            // Close the server socket and return
   118	            if (serverSocket != null) {
   119	                try {
   120	                    serverSocket.close();
   121	                } catch (IOException e) {
   122	                    // Ignore
   123	                }
   124	            }
   125	        }
   126	    }

Этот код не будет анализироваться один за другим, общий эффект такой же, как и в комментарии перед методом, а именно:Этот метод вернется после ожидания, чтобы получить правильную команду отключения. Это было бы главной резьбой в живых - прослушивание HTTP-соединительной резьбы пул - это нить демона".

Если вы знакомы с программированием Socket в Java, вам будет легко понять этот код, который является адресом по умолчанию (значение адреса определяется адресом переменной экземпляра, значение по умолчанию равноlocalhost) порт по умолчанию (значение порта определяется переменной порта экземпляра, которая по умолчанию равна8005) на соединении Socket, когда обнаруживается, что содержимое входного потока отслеживаемого соединения соответствует значению конфигурации по умолчанию (значение по умолчанию равно строкеSHUTDOWN), чтобы выйти из цикла, и метод возвращается (строки 103–107). В противном случае метод будет продолжать выполняться в цикле. Обычно основной поток пользователя блокируется (строка 56) до тех пор, пока не будет доступа.localhost:8005появляется подключение. Из-за этого основной поток, видимый в начале, всегда выполняется, а поскольку этот поток всегда выполняется, всегда будут существовать другие потоки демона.

После разговора о генерации этого потока, давайте посмотрим на закрытие этого потока.Согласно приведенному выше анализу, этот поток обеспечивает механизм закрытия, то есть до тех пор, пока доступlocalhost:8005, и отправить контент какSHUTDOWNстрока, вы можете закрыть его.

Это именно то, что делает Tomcat.Вообще говоря, Tomcat выключается, выполняя сценарий shutdown.bat или shutdown.sh.Для этого сценария, пожалуйста, обратитесь к статье об анализе сценария запуска.Механизм аналогичен и в конечном итоге будет выполнен .org.apache.catalina.startup.BootstrapОсновной метод класса и передача параметров"stop", посмотрите на 2-ю картинку в этой статьеorg.apache.catalina.startup.Bootstrapстрока 458 класса, которая затем вызоветorg.apache.catalina.startup.CatalinaМетод класса stopServer:


     1	    public void stopServer(String[] arguments) {
     2	
     3	        if (arguments != null) {
     4	            arguments(arguments);
     5	        }
     6	
     7	        Server s = getServer();
     8	        if( s == null ) {
     9	            // Create and execute our Digester
    10	            Digester digester = createStopDigester();
    11	            digester.setClassLoader(Thread.currentThread().getContextClassLoader());
    12	            File file = configFile();
    13	            FileInputStream fis = null;
    14	            try {
    15	                InputSource is =
    16	                    new InputSource(file.toURI().toURL().toString());
    17	                fis = new FileInputStream(file);
    18	                is.setByteStream(fis);
    19	                digester.push(this);
    20	                digester.parse(is);
    21	            } catch (Exception e) {
    22	                log.error("Catalina.stop: ", e);
    23	                System.exit(1);
    24	            } finally {
    25	                if (fis != null) {
    26	                    try {
    27	                        fis.close();
    28	                    } catch (IOException e) {
    29	                        // Ignore
    30	                    }
    31	                }
    32	            }
    33	        } else {
    34	            // Server object already present. Must be running as a service
    35	            try {
    36	                s.stop();
    37	            } catch (LifecycleException e) {
    38	                log.error("Catalina.stop: ", e);
    39	            }
    40	            return;
    41	        }
    42	
    43	        // Stop the existing server
    44	        s = getServer();
    45	        if (s.getPort()>0) {
    46	            Socket socket = null;
    47	            OutputStream stream = null;
    48	            try {
    49	                socket = new Socket(s.getAddress(), s.getPort());
    50	                stream = socket.getOutputStream();
    51	                String shutdown = s.getShutdown();
    52	                for (int i = 0; i < shutdown.length(); i++) {
    53	                    stream.write(shutdown.charAt(i));
    54	                }
    55	                stream.flush();
    56	            } catch (ConnectException ce) {
    57	                log.error(sm.getString("catalina.stopServer.connectException",
    58	                                       s.getAddress(),
    59	                                       String.valueOf(s.getPort())));
    60	                log.error("Catalina.stop: ", ce);
    61	                System.exit(1);
    62	            } catch (IOException e) {
    63	                log.error("Catalina.stop: ", e);
    64	                System.exit(1);
    65	            } finally {
    66	                if (stream != null) {
    67	                    try {
    68	                        stream.close();
    69	                    } catch (IOException e) {
    70	                        // Ignore
    71	                    }
    72	                }
    73	                if (socket != null) {
    74	                    try {
    75	                        socket.close();
    76	                    } catch (IOException e) {
    77	                        // Ignore
    78	                    }
    79	                }
    80	            }
    81	        } else {
    82	            log.error(sm.getString("catalina.stopServer"));
    83	            System.exit(1);
    84	        }
    85	    }

Строки с 8 по 41 предназначены для чтения файла конфигурации, вы можете обратиться кПредыдущий анализ статьи Digester,Больше никогда. Начиная со строки 49,localhost:8005Инициируйте соединение Socket и напишитеSHUTDOWNнить. Это закроет единственный пользовательский поток в Tomcat, затем все потоки демона завершатся (гарантировано JVM), а затем все приложение завершит работу.

Механизм отключения Tomcat по умолчанию разобран выше, но он закрывается запуском скрипта, я думаю, что это более хлопотно, поэтому можно ли закрыть Tomcat через метод онлайн-доступа? Конечно, более жестокий геймплей заключается в непосредственном измененииorg.apache.catalina.core.StandardServerСтрока 500 исходного кода будет

boolean match = command.toString().equals(shutdown);

изменить на

boolean match = command.toString().equals(“GET /SHUTDOWN HTTP/1.1”);  

Или измените файл server.xml, найдите узел Сервер и измените исходный

<Server port="8005" shutdown="SHUTDOWN">  

изменить на

<Server port="8005" shutdown="GET /SHUTDOWN HTTP/1.1">  

Это прямо вводится в браузереhttp://localhost:8005/SHUTDOWNМожно Tomcat закрыть, впринципе? После прочтения вышеуказанной статьи это не должно вызвать затруднений.