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:
parent
86c464d15e
commit
1e70d5a39e
|
@ -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>
|
||||||
|
|
|
@ -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() {
|
||||||
|
@ -115,17 +139,23 @@ public class BasicPoolEntry extends AbstractPoolEntry {
|
||||||
public long getExpiry() {
|
public long getExpiry() {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -89,6 +89,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;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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,10 +109,10 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new thread safe connection manager.
|
* Creates a new thread safe connection manager.
|
||||||
*
|
*
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -389,6 +394,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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue