You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2022/02/18 19:02:31 UTC

[tomcat] branch 8.5.x updated: Back-port the enhancement to the graceful close feature. BZ 64080

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/8.5.x by this push:
     new 551061e  Back-port the enhancement to the graceful close feature. BZ 64080
551061e is described below

commit 551061e203f7925a7b6fa074c5d529c8e6ad9ec2
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Feb 18 19:02:17 2022 +0000

    Back-port the enhancement to the graceful close feature. BZ 64080
---
 java/org/apache/catalina/core/StandardService.java | 41 ++++++++++++++++------
 java/org/apache/coyote/AbstractProtocol.java       | 14 ++++++++
 java/org/apache/coyote/LocalStrings.properties     |  1 +
 java/org/apache/coyote/LocalStrings_fr.properties  |  1 +
 java/org/apache/coyote/LocalStrings_ja.properties  |  1 +
 java/org/apache/coyote/LocalStrings_ko.properties  |  1 +
 .../apache/coyote/LocalStrings_zh_CN.properties    |  1 +
 java/org/apache/coyote/ProtocolHandler.java        | 34 ++++++++++++++++++
 .../apache/tomcat/util/net/AbstractEndpoint.java   | 34 +++++++++++++++---
 webapps/docs/changelog.xml                         |  7 ++++
 webapps/docs/config/service.xml                    | 10 ++++++
 11 files changed, 129 insertions(+), 16 deletions(-)

diff --git a/java/org/apache/catalina/core/StandardService.java b/java/org/apache/catalina/core/StandardService.java
index d148eeb..c7616b1 100644
--- a/java/org/apache/catalina/core/StandardService.java
+++ b/java/org/apache/catalina/core/StandardService.java
@@ -100,8 +100,21 @@ public class StandardService extends LifecycleMBeanBase implements Service {
     protected final MapperListener mapperListener = new MapperListener(this);
 
 
+    private long gracefulStopAwaitMillis = 0;
+
+
     // ------------------------------------------------------------- Properties
 
+    public long getGracefulStopAwaitMillis() {
+        return gracefulStopAwaitMillis;
+    }
+
+
+    public void setGracefulStopAwaitMillis(long gracefulStopAwaitMillis) {
+        this.gracefulStopAwaitMillis = gracefulStopAwaitMillis;
+    }
+
+
     @Override
     public Mapper getMapper() {
         return mapper;
@@ -459,20 +472,26 @@ public class StandardService extends LifecycleMBeanBase implements Service {
     @Override
     protected void stopInternal() throws LifecycleException {
 
-        // Pause connectors first
         synchronized (connectorsLock) {
+            // Initiate a graceful stop for each connector
+            // This will only work if the bindOnInit==false which is not the
+            // default.
             for (Connector connector: connectors) {
-                try {
-                    connector.pause();
-                } catch (Exception e) {
-                    log.error(sm.getString(
-                            "standardService.connector.pauseFailed",
-                            connector), e);
-                }
-                // Close server socket if bound on start
-                // Note: test is in AbstractEndpoint
                 connector.getProtocolHandler().closeServerSocketGraceful();
             }
+
+            // Wait for the graceful shutdown to complete
+            long waitMillis = gracefulStopAwaitMillis;
+            if (waitMillis > 0) {
+                for (Connector connector: connectors) {
+                    waitMillis = connector.getProtocolHandler().awaitConnectionsClose(waitMillis);
+                }
+            }
+
+            // Pause the connectors
+            for (Connector connector: connectors) {
+                connector.pause();
+            }
         }
 
         if(log.isInfoEnabled()) {
@@ -480,7 +499,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
         }
         setState(LifecycleState.STOPPING);
 
-        // Stop our defined Container second
+        // Stop our defined Container once the Connectors are all paused
         if (engine != null) {
             synchronized (engine) {
                 engine.stop();
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index 5b10150..59aff52 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -287,6 +287,12 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
     public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
 
 
+    /**
+     * The time Tomcat will wait for a subsequent request before closing the
+     * connection. The default is {@link #getConnectionTimeout()}.
+     *
+     * @return The timeout in milliseconds
+     */
     public int getKeepAliveTimeout() { return endpoint.getKeepAliveTimeout(); }
     public void setKeepAliveTimeout(int keepAliveTimeout) {
         endpoint.setKeepAliveTimeout(keepAliveTimeout);
@@ -710,6 +716,14 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
     }
 
 
+    @Override
+    public long awaitConnectionsClose(long waitMillis) {
+        getLog().info(sm.getString("abstractProtocol.closeConnectionsAwait",
+                Long.valueOf(waitMillis), getName()));
+        return endpoint.awaitConnectionsClose(waitMillis);
+    }
+
+
     // ------------------------------------------- Connection handler base class
 
     protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
diff --git a/java/org/apache/coyote/LocalStrings.properties b/java/org/apache/coyote/LocalStrings.properties
index 7b359fa..a56c2a7 100644
--- a/java/org/apache/coyote/LocalStrings.properties
+++ b/java/org/apache/coyote/LocalStrings.properties
@@ -33,6 +33,7 @@ abstractProcessor.pushrequest.notsupported=Server push requests are not supporte
 abstractProcessor.setErrorState=Error state [{0}] reported while processing request
 abstractProcessor.socket.ssl=Exception getting SSL attributes
 
+abstractProtocol.closeConnectionsAwait=Waiting [{0}] milliseconds for existing connections to [{1}] to complete and close.
 abstractProtocol.mbeanDeregistrationFailed=Failed to deregister MBean named [{0}] from MBean server [{1}]
 abstractProtocol.processorRegisterError=Error registering request processor
 abstractProtocol.processorUnregisterError=Error unregistering request processor
diff --git a/java/org/apache/coyote/LocalStrings_fr.properties b/java/org/apache/coyote/LocalStrings_fr.properties
index 5b1a46f..9373185 100644
--- a/java/org/apache/coyote/LocalStrings_fr.properties
+++ b/java/org/apache/coyote/LocalStrings_fr.properties
@@ -33,6 +33,7 @@ abstractProcessor.pushrequest.notsupported=Le requêtes push du serveur ne sont
 abstractProcessor.setErrorState=Etat d''erreur [{0}] lors du traitement de la requête
 abstractProcessor.socket.ssl=Exception lors de l'obtention des attributs SSL
 
+abstractProtocol.closeConnectionsAwait=Attente de [{0}] millisecondes pour que les connections en cours vers [{1}] soient complètes et fermées
 abstractProtocol.mbeanDeregistrationFailed=Erreur lors du désenregistrement du mbean [{0}] dans le serveur [{1}]
 abstractProtocol.processorRegisterError=Erreur lors de l'enregistrement du processeur de requêtes
 abstractProtocol.processorUnregisterError=Erreur lors du désenregistrement du processeur de requêtes
diff --git a/java/org/apache/coyote/LocalStrings_ja.properties b/java/org/apache/coyote/LocalStrings_ja.properties
index 472ea76..085a461 100644
--- a/java/org/apache/coyote/LocalStrings_ja.properties
+++ b/java/org/apache/coyote/LocalStrings_ja.properties
@@ -33,6 +33,7 @@ abstractProcessor.pushrequest.notsupported=このプロトコルはサーバー
 abstractProcessor.setErrorState=リクエストの処理中にエラー状態[{0}]が報告されました
 abstractProcessor.socket.ssl=SSL属性取得時の例外
 
+abstractProtocol.closeConnectionsAwait=[{1}]への既存の接続が完了して閉じるのを[{0}]ミリ秒待機します。
 abstractProtocol.mbeanDeregistrationFailed=MBeanサーバー[{1}]から[{0}]という名前のMBeanの登録を解除できませんでした。
 abstractProtocol.processorRegisterError=リクエストプロセッサ登録中のエラー
 abstractProtocol.processorUnregisterError=リクエストプロセッサ登録解除中のエラー
diff --git a/java/org/apache/coyote/LocalStrings_ko.properties b/java/org/apache/coyote/LocalStrings_ko.properties
index 9db20d2..1ff7979 100644
--- a/java/org/apache/coyote/LocalStrings_ko.properties
+++ b/java/org/apache/coyote/LocalStrings_ko.properties
@@ -33,6 +33,7 @@ abstractProcessor.pushrequest.notsupported=이 프로토콜은 서버 push 요
 abstractProcessor.setErrorState=요청 처리 중 오류 상태 [{0}]이(가) 보고됨.
 abstractProcessor.socket.ssl=SSL 속성들을 얻으려는 중 예외 발생
 
+abstractProtocol.closeConnectionsAwait=[{1}] 프로토콜에 연결된 기존 연결들이 완료되고 닫히기까지 [{0}] 밀리초 동안 대기합니다.
 abstractProtocol.mbeanDeregistrationFailed=MBean 서버 [{1}](으)로부터, [{0}](이)라는 이름의 MBean의 등록을 제거하지 못했습니다.
 abstractProtocol.processorRegisterError=RequestProcessor 구성요소를 등록하는 중 오류 발생
 abstractProtocol.processorUnregisterError=RequestProcessor 구성요소를 등록 해제하는 중 오류 발생
diff --git a/java/org/apache/coyote/LocalStrings_zh_CN.properties b/java/org/apache/coyote/LocalStrings_zh_CN.properties
index 12b13f1..5fd8e35 100644
--- a/java/org/apache/coyote/LocalStrings_zh_CN.properties
+++ b/java/org/apache/coyote/LocalStrings_zh_CN.properties
@@ -33,6 +33,7 @@ abstractProcessor.pushrequest.notsupported=此协议不支持服务器推送请
 abstractProcessor.setErrorState=正在处理请求时出现错误状态[{0}]
 abstractProcessor.socket.ssl=获取SSL属性异常
 
+abstractProtocol.closeConnectionsAwait=等待[{0}]毫秒,等待到[{1}]的现有连接完成并关闭。
 abstractProtocol.mbeanDeregistrationFailed=无法从MBean服务器[{1}]中注销名为[{0}]的MBean
 abstractProtocol.processorRegisterError=注册请求处理器错误
 abstractProtocol.processorUnregisterError=注销请求处理器错误
diff --git a/java/org/apache/coyote/ProtocolHandler.java b/java/org/apache/coyote/ProtocolHandler.java
index 4e8d7c4..a616347 100644
--- a/java/org/apache/coyote/ProtocolHandler.java
+++ b/java/org/apache/coyote/ProtocolHandler.java
@@ -113,6 +113,19 @@ public interface ProtocolHandler {
 
 
     /**
+     * Wait for the client connections to the server to close gracefully. The
+     * method will return when all of the client connections have closed or the
+     * method has been waiting for {@code waitTimeMillis}.
+     *
+     * @param waitMillis    The maximum time to wait in milliseconds for the
+     *                      client connections to close.
+     *
+     * @return The wait time, if any remaining when the method returned
+     */
+    public long awaitConnectionsClose(long waitMillis);
+
+
+    /**
      * Requires APR/native library
      *
      * @return <code>true</code> if this Protocol Handler requires the
@@ -130,10 +143,31 @@ public interface ProtocolHandler {
     public boolean isSendfileSupported();
 
 
+    /**
+     * Add a new SSL configuration for a virtual host.
+     * @param sslHostConfig the configuration
+     */
     public void addSslHostConfig(SSLHostConfig sslHostConfig);
+
+
+    /**
+     * Find all configured SSL virtual host configurations which will be used
+     * by SNI.
+     * @return the configurations
+     */
     public SSLHostConfig[] findSslHostConfigs();
 
 
+    /**
+     * Add a new protocol for used by HTTP/1.1 upgrade or ALPN.
+     * @param upgradeProtocol the protocol
+     */
     public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol);
+
+
+    /**
+     * Return all configured upgrade protocols.
+     * @return the protocols
+     */
     public UpgradeProtocol[] findUpgradeProtocols();
 }
diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index a580211..f6d4b6d 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -134,10 +134,26 @@ public abstract class AbstractEndpoint<S,U> {
     }
 
     protected enum BindState {
-        UNBOUND,
-        BOUND_ON_INIT,
-        BOUND_ON_START,
-        SOCKET_CLOSED_ON_STOP
+        UNBOUND(false, false),
+        BOUND_ON_INIT(true, true),
+        BOUND_ON_START(true, true),
+        SOCKET_CLOSED_ON_STOP(false, true);
+
+        private final boolean bound;
+        private final boolean wasBound;
+
+        private BindState(boolean bound, boolean wasBound) {
+            this.bound = bound;
+            this.wasBound = wasBound;
+        }
+
+        public boolean isBound() {
+            return bound;
+        }
+
+        public boolean wasBound() {
+            return wasBound;
+        }
     }
 
 
@@ -610,6 +626,9 @@ public abstract class AbstractEndpoint<S,U> {
     public boolean getBindOnInit() { return bindOnInit; }
     public void setBindOnInit(boolean b) { this.bindOnInit = b; }
     private volatile BindState bindState = BindState.UNBOUND;
+    protected BindState getBindState() {
+        return bindState;
+    }
 
     /**
      * Keepalive timeout, if not set the soTimeout is used.
@@ -753,7 +772,12 @@ public abstract class AbstractEndpoint<S,U> {
      */
     private int maxKeepAliveRequests=100; // as in Apache HTTPD server
     public int getMaxKeepAliveRequests() {
-        return maxKeepAliveRequests;
+        // Disable keep-alive if the server socket is not bound
+        if (bindState.isBound()) {
+            return maxKeepAliveRequests;
+        } else {
+            return 1;
+        }
     }
     public void setMaxKeepAliveRequests(int maxKeepAliveRequests) {
         this.maxKeepAliveRequests = maxKeepAliveRequests;
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c307798..691d311 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -108,6 +108,13 @@
   <subsection name="Catalina">
     <changelog>
       <add>
+        <bug>64080</bug>: Enhance the graceful shutdown feature. Includes a new
+        option for <code>StandardService</code>,
+        <code>gracefulStopAwaitMillis</code>, that allows a time to be
+        specified to wait for client connections to complete and close before
+        the Container hierarchy is stopped. (markt/remm)
+      </add>
+      <add>
         Add <code>ha-api-*.jar</code> and <code>jaxws-rt-*.jar</code> to the
         list of JARs to skip when scanning for TLDs, web fragments and
         annotations. (michaelo)
diff --git a/webapps/docs/config/service.xml b/webapps/docs/config/service.xml
index bb8d94d..5fd0acd 100644
--- a/webapps/docs/config/service.xml
+++ b/webapps/docs/config/service.xml
@@ -80,6 +80,16 @@
   common attributes listed above):</p>
 
   <attributes>
+
+    <attribute name="gracefulStopAwaitMillis" required="false">
+      <p>The time to wait, in milliseconds, when stopping the Service for the
+      client connections to finish processing and close before the Service's
+      container hierarchy is stopped. The wait only applies to Connectors
+      configured with a <code>bindOnInit</code> value of <code>false</code>.
+      Any value of zero or less means there will be no wait. If not specified,
+      the default value of zero will be used.</p>
+    </attribute>
+
   </attributes>
 
   </subsection>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org