HTTPCLIENT-1000: Maximum connection lifetimes settings for ThreadSafeClientConnManager.

Contributed by Michajlo Matijkiw <michajlo_matijkiw at comcast.com>



git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1001144 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-09-25 06:35:07 +00:00
parent 86c464d15e
commit 1e70d5a39e
6 changed files with 121 additions and 13 deletions

View File

@ -1,6 +1,9 @@
Changes since 4.1 ALPHA2 Changes since 4.1 ALPHA2
------------------- -------------------
* [HTTPCLIENT-1000] Maximum connection lifetimes settings for ThreadSafeClientConnManager.
Contributed by Michajlo Matijkiw <michajlo_matijkiw at comcast.com>
* [HTTPCLIENT-960] HttpMultipart doesn't generate Content-Type header for binary parts in * [HTTPCLIENT-960] HttpMultipart doesn't generate Content-Type header for binary parts in
BROWSER_COMPATIBLE mode. BROWSER_COMPATIBLE mode.
Contributed by Oleg Kalnichevski <olegk at apache.org> Contributed by Oleg Kalnichevski <olegk at apache.org>

View File

@ -46,6 +46,7 @@ public class BasicPoolEntry extends AbstractPoolEntry {
private final long created; private final long created;
private long updated; private long updated;
private long validUntil;
private long expiry; private long expiry;
/** /**
@ -60,6 +61,8 @@ public class BasicPoolEntry extends AbstractPoolEntry {
throw new IllegalArgumentException("HTTP route may not be null"); throw new IllegalArgumentException("HTTP route may not be null");
} }
this.created = System.currentTimeMillis(); this.created = System.currentTimeMillis();
this.validUntil = Long.MAX_VALUE;
this.expiry = this.validUntil;
} }
/** /**
@ -70,11 +73,32 @@ public class BasicPoolEntry extends AbstractPoolEntry {
*/ */
public BasicPoolEntry(ClientConnectionOperator op, public BasicPoolEntry(ClientConnectionOperator op,
HttpRoute route) { HttpRoute route) {
this(op, route, -1, TimeUnit.MILLISECONDS);
}
/**
* Creates a new pool entry with a specified maximum lifetime.
*
* @param op the connection operator
* @param route the planned route for the connection
* @param connTTL maximum lifetime of this entry, <=0 implies "infinity"
* @param timeunit TimeUnit of connTTL
*
* @since 4.1
*/
public BasicPoolEntry(ClientConnectionOperator op,
HttpRoute route, long connTTL, TimeUnit timeunit) {
super(op, route); super(op, route);
if (route == null) { if (route == null) {
throw new IllegalArgumentException("HTTP route may not be null"); throw new IllegalArgumentException("HTTP route may not be null");
} }
this.created = System.currentTimeMillis(); this.created = System.currentTimeMillis();
if (connTTL > 0) {
this.validUntil = this.created + timeunit.toMillis(connTTL);
} else {
this.validUntil = Long.MAX_VALUE;
}
this.expiry = this.validUntil;
} }
protected final OperatedClientConnection getConnection() { protected final OperatedClientConnection getConnection() {
@ -116,16 +140,22 @@ public class BasicPoolEntry extends AbstractPoolEntry {
return this.expiry; return this.expiry;
} }
public long getValidUntil() {
return this.validUntil;
}
/** /**
* @since 4.1 * @since 4.1
*/ */
public void updateExpiry(long time, TimeUnit timeunit) { public void updateExpiry(long time, TimeUnit timeunit) {
this.updated = System.currentTimeMillis(); this.updated = System.currentTimeMillis();
long newExpiry;
if (time > 0) { if (time > 0) {
this.expiry = this.updated + timeunit.toMillis(time); newExpiry = this.updated + timeunit.toMillis(time);
} else { } else {
this.expiry = Long.MAX_VALUE; newExpiry = Long.MAX_VALUE;
} }
this.expiry = Math.min(validUntil, newExpiry);
} }
/** /**

View File

@ -90,6 +90,10 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
/** Map of route-specific pools */ /** Map of route-specific pools */
protected final Map<HttpRoute, RouteSpecificPool> routeToPool; protected final Map<HttpRoute, RouteSpecificPool> routeToPool;
private final long connTTL;
private final TimeUnit connTTLTimeUnit;
protected volatile boolean shutdown; protected volatile boolean shutdown;
protected volatile int maxTotalConnections; protected volatile int maxTotalConnections;
@ -105,6 +109,18 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
final ClientConnectionOperator operator, final ClientConnectionOperator operator,
final ConnPerRoute connPerRoute, final ConnPerRoute connPerRoute,
int maxTotalConnections) { int maxTotalConnections) {
this(operator, connPerRoute, maxTotalConnections, -1, TimeUnit.MILLISECONDS);
}
/**
* @since 4.1
*/
public ConnPoolByRoute(
final ClientConnectionOperator operator,
final ConnPerRoute connPerRoute,
int maxTotalConnections,
long connTTL,
final TimeUnit connTTLTimeUnit) {
super(); super();
if (operator == null) { if (operator == null) {
throw new IllegalArgumentException("Connection operator may not be null"); throw new IllegalArgumentException("Connection operator may not be null");
@ -120,6 +136,8 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
this.freeConnections = createFreeConnQueue(); this.freeConnections = createFreeConnQueue();
this.waitingThreads = createWaitingThreadQueue(); this.waitingThreads = createWaitingThreadQueue();
this.routeToPool = createRouteToPoolMap(); this.routeToPool = createRouteToPoolMap();
this.connTTL = connTTL;
this.connTTLTimeUnit = connTTLTimeUnit;
} }
protected Lock getLock() { protected Lock getLock() {
@ -532,7 +550,7 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
} }
// the entry will create the connection when needed // the entry will create the connection when needed
BasicPoolEntry entry = new BasicPoolEntry(op, rospl.getRoute()); BasicPoolEntry entry = new BasicPoolEntry(op, rospl.getRoute(), connTTL, connTTLTimeUnit);
poolLock.lock(); poolLock.lock();
try { try {

View File

@ -87,6 +87,20 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
* @param schreg the scheme registry. * @param schreg the scheme registry.
*/ */
public ThreadSafeClientConnManager(final SchemeRegistry schreg) { public ThreadSafeClientConnManager(final SchemeRegistry schreg) {
this(schreg, -1, TimeUnit.MILLISECONDS);
}
/**
* Creates a new thread safe connection manager.
*
* @param schreg the scheme registry.
* @param connTTL max connection lifetime, <=0 implies "infinity"
* @param connTTLTimeUnit TimeUnit of connTTL
*
* @since 4.1
*/
public ThreadSafeClientConnManager(final SchemeRegistry schreg,
long connTTL, TimeUnit connTTLTimeUnit) {
super(); super();
if (schreg == null) { if (schreg == null) {
throw new IllegalArgumentException("Scheme registry may not be null"); throw new IllegalArgumentException("Scheme registry may not be null");
@ -95,7 +109,7 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
this.schemeRegistry = schreg; this.schemeRegistry = schreg;
this.connPerRoute = new ConnPerRouteBean(); this.connPerRoute = new ConnPerRouteBean();
this.connOperator = createConnectionOperator(schreg); this.connOperator = createConnectionOperator(schreg);
this.pool = createConnectionPool() ; this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ;
this.connectionPool = this.pool; this.connectionPool = this.pool;
} }
@ -149,8 +163,8 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
* *
* @since 4.1 * @since 4.1
*/ */
protected ConnPoolByRoute createConnectionPool() { protected ConnPoolByRoute createConnectionPool(long connTTL, TimeUnit connTTLTimeUnit) {
return new ConnPoolByRoute(connOperator, connPerRoute, 20); return new ConnPoolByRoute(connOperator, connPerRoute, 20, connTTL, connTTLTimeUnit);
} }
/** /**

View File

@ -81,9 +81,14 @@ public class TestTSCCMWithServer extends ServerTestBase {
* @return a connection manager to test * @return a connection manager to test
*/ */
public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) { public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) {
return createTSCCM(schreg, -1, TimeUnit.MILLISECONDS);
}
public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg,
long connTTL, TimeUnit connTTLTimeUnit) {
if (schreg == null) if (schreg == null)
schreg = supportedSchemes; schreg = supportedSchemes;
return new ThreadSafeClientConnManager(schreg); return new ThreadSafeClientConnManager(schreg, connTTL, connTTLTimeUnit);
} }
/** /**
@ -354,7 +359,7 @@ public class TestTSCCMWithServer extends ServerTestBase {
} }
@Test @Test
public void testCloseExpiredConnections() throws Exception { public void testCloseExpiredIdleConnections() throws Exception {
ThreadSafeClientConnManager mgr = createTSCCM(null); ThreadSafeClientConnManager mgr = createTSCCM(null);
mgr.setMaxTotal(1); mgr.setMaxTotal(1);
@ -390,6 +395,44 @@ public class TestTSCCMWithServer extends ServerTestBase {
mgr.shutdown(); mgr.shutdown();
} }
@Test
public void testCloseExpiredTTLConnections() throws Exception {
ThreadSafeClientConnManager mgr = createTSCCM(null, 100, TimeUnit.MILLISECONDS);
mgr.setMaxTotal(1);
final HttpHost target = getServerHttp();
final HttpRoute route = new HttpRoute(target, null, false);
ManagedClientConnection conn = getConnection(mgr, route);
conn.open(route, httpContext, defaultParams);
Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
// Release, let remain idle for forever
mgr.releaseConnection(conn, -1, TimeUnit.MILLISECONDS);
// Released, still active.
Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
mgr.closeExpiredConnections();
// Time has not expired yet.
Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
Thread.sleep(150);
mgr.closeExpiredConnections();
// TTL expired now, connections are destroyed.
Assert.assertEquals("connectionsInPool", 0, mgr.getConnectionsInPool());
Assert.assertEquals("connectionsInPool(host)", 0, mgr.getConnectionsInPool(route));
mgr.shutdown();
}
/** /**
* Tests releasing connection from #abort method called from the * Tests releasing connection from #abort method called from the
* main execution thread while there is no blocking I/O operation. * main execution thread while there is no blocking I/O operation.

View File

@ -27,20 +27,20 @@
package org.apache.http.impl.conn.tsccm; package org.apache.http.impl.conn.tsccm;
import java.util.concurrent.locks.Lock; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.conn.ClientConnectionOperator; import org.apache.http.conn.ClientConnectionOperator;
import org.apache.http.conn.ClientConnectionRequest; import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ManagedClientConnection; import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory; import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.impl.conn.GetConnThread; import org.apache.http.impl.conn.GetConnThread;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -101,7 +101,7 @@ public class TestSpuriousWakeup {
} }
@Override @Override
protected ConnPoolByRoute createConnectionPool() { protected ConnPoolByRoute createConnectionPool(long connTTL, TimeUnit connTTLUnit) {
extendedCPBR = new XConnPoolByRoute(connOperator, connPerRoute, 20); extendedCPBR = new XConnPoolByRoute(connOperator, connPerRoute, 20);
// no connection GC required // no connection GC required
return extendedCPBR; return extendedCPBR;