-
Notifications
You must be signed in to change notification settings - Fork 81
feat: add TcpStackAwareSocketChannel for z/OS TCP/IP stack recovery — GH#4776 #4792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3.x.x
Are you sure you want to change the base?
Changes from all commits
5de0b3e
904c283
5d653b2
fb7e318
208847d
0dfba61
2d9d8d5
fac753c
5b61bf6
f91afee
67c2e81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| import org.apache.coyote.ProtocolHandler; | ||
| import org.apache.tomcat.util.net.AbstractEndpoint; | ||
| import org.apache.tomcat.util.net.NioEndpoint; | ||
| import org.apache.tomcat.util.net.SocketWrapperBase; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; | ||
| import org.springframework.context.annotation.Bean; | ||
|
|
@@ -54,8 +55,9 @@ public class TomcatAcceptFixConfig { | |
| private static final Field ENDPOINT_FIELD; | ||
| private static final Field NIO_SOCKET_FIELD; | ||
|
|
||
| private static final MethodHandle IMPL_CLOSE_SELECTABGLE_CHANNEL_HANLE; // NOSONAR | ||
| private static final MethodHandle IMPL_CLOSE_SELECTABLE_CHANNEL_HANDLE; // NOSONAR | ||
| private static final MethodHandle IMPL_CONFIGURE_BLOCKING; // NOSONAR | ||
| private static final String NETWORK_RECYCLED_EXCEPTION_CLASS = "com.ibm.net.NetworkRecycledException"; | ||
|
|
||
| /** | ||
| * To mitigate parallel treatment of socket rebinding | ||
|
|
@@ -69,7 +71,7 @@ public class TomcatAcceptFixConfig { | |
|
|
||
| Method implCloseSelectableChannel = AbstractSelectableChannel.class.getDeclaredMethod("implCloseSelectableChannel"); | ||
| implCloseSelectableChannel.setAccessible(true); // NOSONAR | ||
| IMPL_CLOSE_SELECTABGLE_CHANNEL_HANLE = MethodHandles.lookup().unreflect(implCloseSelectableChannel); | ||
| IMPL_CLOSE_SELECTABLE_CHANNEL_HANDLE = MethodHandles.lookup().unreflect(implCloseSelectableChannel); | ||
|
|
||
| Method implConfigureBlocking = AbstractSelectableChannel.class.getDeclaredMethod("implConfigureBlocking", boolean.class); | ||
| implConfigureBlocking.setAccessible(true); // NOSONAR | ||
|
|
@@ -143,12 +145,32 @@ public void stopping() { | |
| running.set(false); | ||
| } | ||
|
|
||
| static boolean isRecycledClass(Throwable t) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is recycled is called only once. Can be internalized. |
||
| return NETWORK_RECYCLED_EXCEPTION_CLASS.equals(t.getClass().getName()); | ||
| } | ||
|
|
||
| static boolean isTcpStackRestarted(Throwable t) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was the method moved from FixedServerSocketChannel class? Seems unnecessary as the method is called only from there. |
||
| if ((t.getMessage() != null) && t.getMessage().contains("EDC5122I")) { | ||
| return true; | ||
| } | ||
|
|
||
| if (isRecycledClass(t)) { | ||
| return true; | ||
| } | ||
|
|
||
| Throwable cause = t.getCause(); | ||
| if ((cause != null) && (cause != t)) { | ||
|
Comment on lines
+161
to
+162
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cause == null if t == cause from the t.getClause implementation. The cause != t check is unnecesary |
||
| return isTcpStackRestarted(cause); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Socket implementation wrapper to handle rebinding on TCP Stack restart | ||
| */ | ||
| class FixedServerSocketChannel extends ServerSocketChannel { | ||
|
|
||
| private static final String NETWORK_RECYCLED_EXCEPTION_CLASS = "com.ibm.net.NetworkRecycledException"; | ||
|
|
||
| /** | ||
| * Wrapper server socket inside | ||
|
|
@@ -181,7 +203,7 @@ class FixedServerSocketChannel extends ServerSocketChannel { | |
| @Override | ||
| protected void implCloseSelectableChannel() throws IOException { | ||
| try { | ||
| IMPL_CLOSE_SELECTABGLE_CHANNEL_HANLE.invoke(socket); | ||
| IMPL_CLOSE_SELECTABLE_CHANNEL_HANDLE.invoke(socket); | ||
| } catch (IOException | RuntimeException e) { | ||
| throw e; | ||
| } catch (Throwable t) { | ||
|
|
@@ -222,6 +244,21 @@ private void bindWithWait() throws IOException, InterruptedException { | |
| } | ||
| } | ||
|
|
||
| private void closeConnectionsAfterTcpStackRestart() { | ||
| int closed = 0; | ||
| int failed = 0; | ||
| for (SocketWrapperBase<?> connection : abstractEndpoint.getConnections()) { | ||
| try { | ||
| connection.close(); | ||
| closed++; | ||
| } catch (RuntimeException e) { | ||
| failed++; | ||
| log.debug("Unable to close a stale client connection after TCP/IP stack restart", e); | ||
| } | ||
| } | ||
| log.info("Closed {} client connection(s) after TCP/IP stack restart; {} could not be closed", closed, failed); | ||
| } | ||
|
|
||
| /** | ||
| * Rebind the server socket. The action could be done just by one thread. Other treats are waiting to be finish | ||
| * by first one. | ||
|
|
@@ -233,6 +270,8 @@ private void bindWithWait() throws IOException, InterruptedException { | |
| private synchronized void rebind(int stateBefore) throws IOException { | ||
| if (state.compareAndSet(stateBefore, stateBefore + 1)) { | ||
| try { | ||
| closeConnectionsAfterTcpStackRestart(); | ||
|
|
||
| // socket must be closed before new binding | ||
| socket.close(); | ||
|
|
||
|
|
@@ -249,34 +288,13 @@ private synchronized void rebind(int stateBefore) throws IOException { | |
| } | ||
| } | ||
|
|
||
| boolean isRecycledClass(Throwable t) { | ||
| return NETWORK_RECYCLED_EXCEPTION_CLASS.equals(t.getClass().getName()); | ||
| } | ||
|
|
||
| boolean isTcpStackRestarted(Throwable t) { | ||
| if ((t.getMessage() != null) && t.getMessage().contains("EDC5122I")) { | ||
| return true; | ||
| } | ||
|
|
||
| if (isRecycledClass(t)) { | ||
| return true; | ||
| } | ||
|
|
||
| Throwable cause = t.getCause(); | ||
| if ((cause != null) && (cause != t)) { | ||
| return isTcpStackRestarted(cause); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| public SocketChannel accept() throws IOException { | ||
| // obtain current state of rebinding to detection parallel actions | ||
| final int stateBefore = state.get(); | ||
| try { | ||
| return socket.accept(); | ||
| } catch (IOException ioe) { | ||
| if (isTcpStackRestarted(ioe)) { | ||
| if (TomcatAcceptFixConfig.isTcpStackRestarted(ioe)) { | ||
| // the fix solve just one issue about stopped TCP/IP stack | ||
| log.debug("The TCP/IP stack was probably restarted. The socket of Tomcat will rebind."); | ||
| rebind(stateBefore); | ||
|
|
@@ -289,7 +307,7 @@ public SocketChannel accept() throws IOException { | |
| } | ||
|
|
||
| /** | ||
| * The list of final methods, which cannot be delegated. See {@link FixedServerSocketChannel#socket} | ||
| * Methods final on {@link ServerSocketChannel} that Lombok must not delegate. | ||
| */ | ||
| private interface Overridden { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| /* | ||
| * This program and the accompanying materials are made available under the terms of the | ||
| * Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-v20.html | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Copyright Contributors to the Zowe Project. | ||
| */ | ||
|
|
||
| package com.ibm.net; | ||
|
|
||
| public class NetworkRecycledException extends RuntimeException { | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.