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