com.amazonaws.http.IdleConnectionReaper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-java-sdk-core Show documentation
Show all versions of aws-java-sdk-core Show documentation
The AWS SDK for Java - Core module holds the classes that are used by the individual service clients to interact with Amazon Web Services. Users need to depend on aws-java-sdk artifact for accessing individual client classes.
/*
* Copyright 2012-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amazonaws.http;
import com.amazonaws.annotation.SdkInternalApi;
import com.amazonaws.annotation.SdkTestInternalApi;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.conn.HttpClientConnectionManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* Daemon thread to periodically check connection pools for idle connections.
*
* Connections sitting around idle in the HTTP connection pool for too long will
* eventually be terminated by the AWS end of the connection, and will go into
* CLOSE_WAIT. If this happens, sockets will sit around in CLOSE_WAIT, still
* using resources on the client side to manage that socket. Many sockets stuck
* in CLOSE_WAIT can prevent the OS from creating new connections.
*
* This class closes idle connections before they can move into the CLOSE_WAIT
* state.
*
* This thread is important because by default, we disable Apache HttpClient's
* stale connection checking, so without this thread running in the background,
* cleaning up old/inactive HTTP connections, we'd see more IO exceptions when
* stale connections (i.e. closed on the AWS side) are left in the connection
* pool, and requests grab one of them to begin executing a request.
*/
@SdkInternalApi
public final class IdleConnectionReaper extends Thread {
/**
* Shared log for any errors during connection reaping.
*/
private static final Log LOG = LogFactory.getLog(IdleConnectionReaper.class);
/**
* The period between invocations of the idle connection reaper.
*/
private static final int PERIOD_MILLISECONDS = 1000 * 60;
/**
* Legacy constant used when {@link #registerConnectionManager(HttpClientConnectionManager)} is called. New code paths should
* use {@link #registerConnectionManager(HttpClientConnectionManager, long)} and provide the max idle timeout for that
* particular connection manager.
*/
@Deprecated
private static final int DEFAULT_MAX_IDLE_MILLIS = 1000 * 60;
private static final Map connectionManagers = new ConcurrentHashMap();
/**
* Singleton instance of the connection reaper.
*/
private static volatile IdleConnectionReaper instance;
/**
* Set to true when shutting down the reaper; Once set to true, this
* flag is never set back to false.
*/
private volatile boolean shuttingDown;
/**
* Private constructor - singleton pattern.
*/
private IdleConnectionReaper() {
super("java-sdk-http-connection-reaper");
setDaemon(true);
}
/**
* Registers the given connection manager with this reaper.
*
* @return true if the connection manager has been successfully registered; false otherwise.
* @deprecated By {@link #registerConnectionManager(HttpClientConnectionManager, long)}.
*/
@Deprecated
public static boolean registerConnectionManager(HttpClientConnectionManager connectionManager) {
return registerConnectionManager(connectionManager, DEFAULT_MAX_IDLE_MILLIS);
}
/**
* Registers the given connection manager with this reaper;
*
* @param connectionManager Connection manager to register
* @param maxIdleInMs Max idle connection timeout in milliseconds for this connection manager.
* @return true if the connection manager has been successfully registered; false otherwise.
*/
public static boolean registerConnectionManager(HttpClientConnectionManager connectionManager, long maxIdleInMs) {
if (instance == null) {
synchronized (IdleConnectionReaper.class) {
if (instance == null) {
instance = new IdleConnectionReaper();
instance.start();
}
}
}
return connectionManagers.put(connectionManager, maxIdleInMs) == null;
}
/**
* Removes the given connection manager from this reaper,
* and shutting down the reaper if there is zero connection manager left.
*
* @return true if the connection manager has been successfully removed;
* false otherwise.
*/
public static boolean removeConnectionManager(HttpClientConnectionManager connectionManager) {
boolean wasRemoved = connectionManagers.remove(connectionManager) != null;
if (connectionManagers.isEmpty()) {
shutdown();
}
return wasRemoved;
}
@SdkTestInternalApi
public static List getRegisteredConnectionManagers() {
return new ArrayList(connectionManagers.keySet());
}
/**
* Shuts down the thread, allowing the class and instance to be collected.
*
* Since this is a daemon thread, its running will not prevent JVM shutdown.
* It will, however, prevent this class from being unloaded or garbage
* collected, in the context of a long-running application, until it is
* interrupted. This method will stop the thread's execution and clear its
* state. Any use of a service client will cause the thread to be restarted.
*
* @return true if an actual shutdown has been made; false otherwise.
*/
public static synchronized boolean shutdown() {
if (instance != null) {
instance.markShuttingDown();
instance.interrupt();
connectionManagers.clear();
instance = null;
return true;
}
return false;
}
/**
* For testing purposes.
* Returns the number of connection managers currently monitored by this
* reaper.
*/
static int size() {
return connectionManagers.size();
}
private void markShuttingDown() {
shuttingDown = true;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
while (!shuttingDown) {
try {
for (Map.Entry entry : connectionManagers.entrySet()) {
// When we release connections, the connection manager leaves them
// open so they can be reused. We want to close out any idle
// connections so that they don't sit around in CLOSE_WAIT.
try {
entry.getKey().closeIdleConnections(entry.getValue(), TimeUnit.MILLISECONDS);
} catch (Exception t) {
LOG.warn("Unable to close idle connections", t);
}
}
Thread.sleep(PERIOD_MILLISECONDS);
} catch (Throwable t) {
LOG.debug("Reaper thread: ", t);
}
}
LOG.debug("Shutting down reaper thread.");
}
}