Skip to content
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

SimplePool v2 #1963

Merged
merged 6 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.networknt.client.ClientConfig;
import com.networknt.client.Http2Client;
import com.networknt.client.simplepool.SimpleConnectionHolder;
import com.networknt.client.simplepool.SimpleConnectionState;
import com.networknt.client.simplepool.SimpleConnectionPool;
import com.networknt.client.simplepool.undertow.SimpleClientConnectionMaker;
import com.networknt.client.simplepool.undertow.SimpleUndertowConnectionMaker;
import com.networknt.cluster.Cluster;
import com.networknt.config.Config;
import com.networknt.exception.ClientException;
Expand Down Expand Up @@ -88,7 +88,7 @@ public class OauthHelper {
private static final Logger logger = LoggerFactory.getLogger(OauthHelper.class);

private static final SimpleConnectionPool pool = new SimpleConnectionPool(
ClientConfig.get().getConnectionExpireTime(), ClientConfig.get().getConnectionPoolSize(), SimpleClientConnectionMaker.instance());
ClientConfig.get().getConnectionExpireTime(), ClientConfig.get().getConnectionPoolSize(), SimpleUndertowConnectionMaker.instance());

/**
* @deprecated As of release 1.5.29, replaced with @link #getTokenResult(TokenRequest tokenRequest)
Expand Down Expand Up @@ -413,7 +413,7 @@ public static String getKey(KeyRequest keyRequest, String envTag) throws ClientE
final Http2Client client = Http2Client.getInstance();
final CountDownLatch latch = new CountDownLatch(1);
ClientConnection connection = null;
SimpleConnectionHolder.ConnectionToken borrowToken = null;
SimpleConnectionState.ConnectionToken borrowToken = null;

long connectionTimeout = Math.max(2, keyRequest.getKeyConnectionTimeout() / 1000);
long populateKeyTimeout = Math.max(2, keyRequest.getPopulateKeyTimeout() / 1000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

/***
* SimpleConnection is an interface that contains all the required functions and properties of
* a connection that are needed by the SimpleConnectionHolder, SimpleURIConnectionPool, and
* a connection that are needed by the SimpleConnectionState, SimpleURIConnectionPool, and
* SimpleConnectionPool classes.
*
* Concrete HTTP network connections (like Undertow's ClientConnection class) should be wrapped in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,46 @@

/***
* A factory that creates raw connections and wraps them in SimpleConnection objects.
* SimpleConnectionMakers are used by SimpleConnectionHolders to create connections.
* SimpleConnectionMakers are used by SimpleConnectionStates to create connections.
*
*/
public interface SimpleConnectionMaker {
public SimpleConnection makeConnection(long createConnectionTimeout, boolean isHttp2, final URI uri, final Set<SimpleConnection> allCreatedConnections);
public SimpleConnection reuseConnection(long createConnectionTimeout, SimpleConnection connection) throws RuntimeException;
/***
* Establishes a new connection to a URI.
* Implementations of SimpleConnectionMaker are used by SimpleConnectionStates as a connection factory.
*
* @param createConnectionTimeout the maximum time in seconds to wait for a connection to be established
* @param isHttp2 if true, SimpleConnectionMaker must attempt to establish an HTTP/2 connection, otherwise it will
* attempt to create an HTTP/1.1 connection
* @param uri the URI to connect to
* @param allCreatedConnections Implementations of SimpleConnectionMaker are used by SimpleConnectionState to create
* connections to arbitrary URIs. In other words, implementations of SimpleConnectionMaker are used by
* SimpleConnectionState as a connection factory.
*
* A SimpleConnectionMaker will add all connections it creates to the Set <code>allCreatedConnections</code>.
* SimpleURIConnectionPool will compare the connections in this Set to those that it is tracking and close
* any untracked connections.
*
* Untracked connections can occur if there is a connection creation timeout. When such a timeout occurs,
* makeConnection() must throw a RuntimeException which will prevent SimpleURIConnectionPool from acquiring
* a SimpleConnection. However, the connection creation callback thread in makeConnection() may continue to
* execute after the timeout and ultimately succeed in creating the connection after the timeout has occurred
* and the exception has been thrown. Connections that are created but were not returned to
* SimpleURIConnectionPool are considered to be 'untracked'.
*
* Despite not being tracked by SimpleURIConnectionPool, all successfully created connections must be added
* to <code>allCreatedConnections</code>.
*
* SimpleURIConnectionPool prevents these untracked connections from accumulating and causing a connection
* leak over time, by periodically closing any open connections in allCreatedConnections that it is not tracking.
*
* Thread Safety:
* allCreatedConnections MUST be a threadsafe Set, or the thread safety of the connection pool cannot
* be guaranteed.
*
* @return A SimpleConnection to the specified URI
* @throws RuntimeException thrown if the connection establishment timeout (<code>createConnectionTimeout</code>)
* expires before a connection to the URI is established, or if there is an error establishing the connection
*/
public SimpleConnection makeConnection(long createConnectionTimeout, boolean isHttp2, final URI uri, final Set<SimpleConnection> allCreatedConnections) throws RuntimeException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,63 @@
import java.util.concurrent.ConcurrentHashMap;

/**
* This is a class through which multiple URI Connection Pools can be accessed
* SimpleConnectionPool is a connection pool, which means that it manages a pool of reusable network connections.
* Threads borrow connections from the pool, use them, and then return them back to the pool. Once returned, these
* connections can then be borrowed by other threads. Since these returned connections are already active / established,
* they can be used immediately without going through the lengthy connection establishment process. This can very
* significantly increase the performance of systems that make many simultaneous outgoing API calls.
*/
public final class SimpleConnectionPool {
private final Map<URI, SimpleURIConnectionPool> pools = new ConcurrentHashMap<>();
private final SimpleConnectionMaker connectionMaker;
private final long expireTime;
private final int poolSize;

/***
* Creates a SimpleConnectionPool
*
* @param expireTime the length of time in milliseconds a connection is eligible to be borrowed
* @param poolSize the maximum number of unexpired connections the pool can hold at a given time
* @param connectionMaker a class that SimpleConnectionPool uses to create new connections
*/
public SimpleConnectionPool(long expireTime, int poolSize, SimpleConnectionMaker connectionMaker) {
this.expireTime = expireTime;
this.poolSize = poolSize;
this.connectionMaker = connectionMaker;
}

public SimpleConnectionHolder.ConnectionToken borrow(long createConnectionTimeout, boolean isHttp2, URI uri)
throws RuntimeException
/***
* Returns a network connection to a URI
*
* @param createConnectionTimeout The maximum time in seconds to wait for a new connection to be established before
* throwing an exception
* @param isHttp2 if true, SimpleURIConnectionPool will attempt to establish an HTTP/2 connection, otherwise it will
* attempt to create an HTTP/1.1 connection
* @return a ConnectionToken object that contains the borrowed connection. The thread using the connection must
* return this connection to the pool when it is done with it by calling the borrow() method with the
* ConnectionToken as the argument
* @throws RuntimeException if connection creation takes longer than <code>createConnectionTimeout</code> seconds,
* or other issues that prevent connection creation
*/
public SimpleConnectionState.ConnectionToken borrow(long createConnectionTimeout, boolean isHttp2, URI uri)
throws RuntimeException
{
if(!pools.containsKey(uri))
pools.computeIfAbsent(uri, pool -> new SimpleURIConnectionPool(uri, expireTime, poolSize, connectionMaker));

return pools.get(uri).borrow(createConnectionTimeout, isHttp2);
}

public void restore(SimpleConnectionHolder.ConnectionToken connectionToken) {
/***
* Restores borrowed connections
*
* NOTE: A connection that unexpectedly closes may be removed from connection pool tracking before all of its
* ConnectionTokens have been restored. This can result in seeing log messages about CLOSED connections
* being restored to the pool that are no longer tracked / known by the connection pool
*
* @param connectionToken the connection token that represents the borrowing of a connection by a thread
*/
public void restore(SimpleConnectionState.ConnectionToken connectionToken) {
if(connectionToken == null)
return;

Expand Down
Loading