You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by jo...@apache.org on 2011/07/18 18:13:46 UTC
svn commit: r1147947 - in /httpcomponents/httpclient/trunk/httpclient/src:
main/java/org/apache/http/client/ main/java/org/apache/http/impl/client/
main/java/org/apache/http/impl/conn/tsccm/
test/java/org/apache/http/impl/client/
Author: jonm
Date: Mon Jul 18 16:13:44 2011
New Revision: 1147947
URL: http://svn.apache.org/viewvc?rev=1147947&view=rev
Log:
HTTPCLIENT-1101: adaptive connection pool sizing. This implements an
additive increase / multiplicative decrease (AIMD) strategy for
on-the-fly connection pool sizing. No default behavior is changed
at the moment (you have to opt into this algorithm). Still needs
documentation.
Added:
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/BackoffManager.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/Clock.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java (with props)
httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java (with props)
Modified:
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/BackoffManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/BackoffManager.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/BackoffManager.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/BackoffManager.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,28 @@
+package org.apache.http.client;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * Represents a controller that dynamically adjusts the size
+ * of an available connection pool based on feedback from
+ * using the connections.
+ *
+ * @since 4.2
+ *
+ */
+public interface BackoffManager {
+
+ /**
+ * Called when we have decided that the result of
+ * using a connection should be interpreted as a
+ * backoff signal.
+ */
+ public void backOff(HttpRoute route);
+
+ /**
+ * Called when we have determined that the result of
+ * using a connection has succeeded and that we may
+ * probe for more connections.
+ */
+ public void probe(HttpRoute route);
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/BackoffManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,38 @@
+package org.apache.http.client;
+
+import org.apache.http.HttpResponse;
+
+/**
+ * When managing a dynamic number of connections for a given route, this
+ * strategy assesses whether a given request execution outcome should
+ * result in a backoff signal or not, based on either examining the
+ * <code>Throwable</code> that resulted or by examining the resulting
+ * response (e.g. for its status code).
+ *
+ * @since 4.2
+ *
+ */
+public interface ConnectionBackoffStrategy {
+
+ /**
+ * Determines whether seeing the given <code>Throwable</code> as
+ * a result of request execution should result in a backoff
+ * signal.
+ * @param t the <code>Throwable</code> that happened
+ * @return <code>true</code> if a backoff signal should be
+ * given
+ */
+ boolean shouldBackoff(Throwable t);
+
+ /**
+ * Determines whether receiving the given {@link HttpResponse} as
+ * a result of request execution should result in a backoff
+ * signal. Implementations MUST restrict themselves to examining
+ * the response header and MUST NOT consume any of the response
+ * body, if any.
+ * @param t the <code>HttpResponse</code> that was received
+ * @return <code>true</code> if a backoff signal should be
+ * given
+ */
+ boolean shouldBackoff(HttpResponse resp);
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,124 @@
+package org.apache.http.impl.client;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.client.BackoffManager;
+import org.apache.http.conn.params.ConnPerRouteBean;
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * The <code>AIMDBackoffManager</code> applies an additive increase,
+ * multiplicative decrease (AIMD) to managing a dynamic limit to
+ * the number of connections allowed to a given host.
+ *
+ * @since 4.2
+ */
+public class AIMDBackoffManager implements BackoffManager {
+
+ private ConnPerRouteBean connPerRoute;
+ private Clock clock;
+ private long coolDown = 5 * 1000L;
+ private double backoffFactor = 0.5;
+ private int cap = ConnPerRouteBean.DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
+ private Map<HttpRoute,Long> lastRouteProbes =
+ new HashMap<HttpRoute,Long>();
+ private Map<HttpRoute,Long> lastRouteBackoffs =
+ new HashMap<HttpRoute,Long>();
+
+
+ /**
+ * Creates an <code>AIMDBackoffManager</code> to manage
+ * per-host connection pool sizes represented by the
+ * given {@link ConnPerRouteBean}.
+ * @param connPerRoute per-host routing maximums to
+ * be managed
+ */
+ public AIMDBackoffManager(ConnPerRouteBean connPerRoute) {
+ this(connPerRoute, new SystemClock());
+ }
+
+ AIMDBackoffManager(ConnPerRouteBean connPerRoute, Clock clock) {
+ this.clock = clock;
+ this.connPerRoute = connPerRoute;
+ }
+
+ public void backOff(HttpRoute route) {
+ synchronized(connPerRoute) {
+ int curr = connPerRoute.getMaxForRoute(route);
+ Long lastUpdate = getLastUpdate(lastRouteBackoffs, route);
+ long now = clock.getCurrentTime();
+ if (now - lastUpdate < coolDown) return;
+ connPerRoute.setMaxForRoute(route, getBackedOffPoolSize(curr));
+ lastRouteBackoffs.put(route, now);
+ }
+ }
+
+ private int getBackedOffPoolSize(int curr) {
+ if (curr <= 1) return 1;
+ return (int)(Math.floor(backoffFactor * curr));
+ }
+
+ public void probe(HttpRoute route) {
+ synchronized(connPerRoute) {
+ int curr = connPerRoute.getMaxForRoute(route);
+ int max = (curr >= cap) ? cap : curr + 1;
+ Long lastProbe = getLastUpdate(lastRouteProbes, route);
+ Long lastBackoff = getLastUpdate(lastRouteBackoffs, route);
+ long now = clock.getCurrentTime();
+ if (now - lastProbe < coolDown || now - lastBackoff < coolDown)
+ return;
+ connPerRoute.setMaxForRoute(route, max);
+ lastRouteProbes.put(route, now);
+ }
+ }
+
+ private Long getLastUpdate(Map<HttpRoute,Long> updates, HttpRoute route) {
+ Long lastUpdate = updates.get(route);
+ if (lastUpdate == null) lastUpdate = 0L;
+ return lastUpdate;
+ }
+
+ /**
+ * Sets the factor to use when backing off; the new
+ * per-host limit will be roughly, the current max times
+ * this factor. <code>Math.floor</code> is applied in the
+ * case of non-integer outcomes to ensure we actually
+ * decrease the pool size. Pool sizes are never decreased
+ * below 1, however. Defaults to 0.5.
+ * @param d must be between 0.0 and 1.0, exclusive.
+ */
+ public void setBackoffFactor(double d) {
+ if (d <= 0.0 || d >= 1.0) {
+ throw new IllegalArgumentException("backoffFactor must be 0.0 < f < 1.0");
+ }
+ backoffFactor = d;
+ }
+
+ /**
+ * Sets the amount of time, in milliseconds, to wait between
+ * adjustments in pool sizes for a given host, to allow
+ * enough time for the adjustments to take effect. Defaults
+ * to 5000L (5 seconds).
+ * @param l must be positive
+ */
+ public void setCooldownMillis(long l) {
+ if (coolDown <= 0) {
+ throw new IllegalArgumentException("cooldownMillis must be positive");
+ }
+ coolDown = l;
+ }
+
+ /**
+ * Sets the absolute maximum per-host connection pool size to
+ * probe up to; defaults to 2 (the default per-host max).
+ * @param cap must be >= 1
+ */
+ public void setPerHostConnectionCap(int cap) {
+ if (cap < 1) {
+ throw new IllegalArgumentException("perHostConnectionCap must be >= 1");
+ }
+ this.cap = cap;
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java?rev=1147947&r1=1147946&r2=1147947&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java Mon Jul 18 16:13:44 2011
@@ -45,7 +45,9 @@ import org.apache.http.annotation.Guarde
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.auth.AuthSchemeRegistry;
import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.BackoffManager;
import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.ConnectionBackoffStrategy;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
@@ -64,6 +66,7 @@ import org.apache.http.client.utils.URIU
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ClientConnectionManagerFactory;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.cookie.CookieSpecRegistry;
@@ -248,7 +251,14 @@ public abstract class AbstractHttpClient
@GuardedBy("this")
private UserTokenHandler userTokenHandler;
-
+ /** The connection backoff strategy. */
+ @GuardedBy("this")
+ private ConnectionBackoffStrategy connectionBackoffStrategy;
+
+ /** The backoff manager. */
+ @GuardedBy("this")
+ private BackoffManager backoffManager;
+
/**
* Creates a new HTTP client.
*
@@ -363,6 +373,16 @@ public abstract class AbstractHttpClient
return registry;
}
+ protected BackoffManager createBackoffManager() {
+ return new BackoffManager() {
+ public void backOff(HttpRoute ignored) { }
+ public void probe(HttpRoute ignored) { }
+ };
+ }
+
+ protected ConnectionBackoffStrategy createConnectionBackoffStrategy() {
+ return new NullBackoffStrategy();
+ }
protected HttpRequestExecutor createRequestExecutor() {
return new HttpRequestExecutor();
@@ -461,13 +481,22 @@ public abstract class AbstractHttpClient
supportedAuthSchemes = createAuthSchemeRegistry();
}
return supportedAuthSchemes;
- }
-
-
+ }
+
public synchronized void setAuthSchemes(final AuthSchemeRegistry authSchemeRegistry) {
supportedAuthSchemes = authSchemeRegistry;
}
+ public synchronized final ConnectionBackoffStrategy getConnectionBackoffStrategy() {
+ if (connectionBackoffStrategy == null) {
+ connectionBackoffStrategy = createConnectionBackoffStrategy();
+ }
+ return connectionBackoffStrategy;
+ }
+
+ public synchronized void setConnectionBackoffStrategy(final ConnectionBackoffStrategy strategy) {
+ connectionBackoffStrategy = strategy;
+ }
public synchronized final CookieSpecRegistry getCookieSpecs() {
if (supportedCookieSpecs == null) {
@@ -476,12 +505,21 @@ public abstract class AbstractHttpClient
return supportedCookieSpecs;
}
-
+ public synchronized final BackoffManager getBackoffManager() {
+ if (backoffManager == null) {
+ backoffManager = createBackoffManager();
+ }
+ return backoffManager;
+ }
+
+ public synchronized void setBackoffManager(final BackoffManager manager) {
+ backoffManager = manager;
+ }
+
public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
supportedCookieSpecs = cookieSpecRegistry;
}
-
public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
if (reuseStrategy == null) {
reuseStrategy = createConnectionReuseStrategy();
@@ -817,7 +855,33 @@ public abstract class AbstractHttpClient
}
try {
- return director.execute(target, request, execContext);
+ HttpHost targetForRoute = (target != null) ? target
+ : (HttpHost) determineParams(request).getParameter(
+ ClientPNames.DEFAULT_HOST);
+ HttpRoute route = getRoutePlanner().determineRoute(targetForRoute, request, execContext);
+
+ HttpResponse out;
+ try {
+ out = director.execute(target, request, execContext);
+ } catch (RuntimeException re) {
+ if (getConnectionBackoffStrategy().shouldBackoff(re)) {
+ getBackoffManager().backOff(route);
+ }
+ throw re;
+ } catch (Exception e) {
+ if (getConnectionBackoffStrategy().shouldBackoff(e)) {
+ getBackoffManager().backOff(route);
+ }
+ if (e instanceof HttpException) throw (HttpException)e;
+ if (e instanceof IOException) throw (IOException)e;
+ throw new RuntimeException("unexpected exception", e);
+ }
+ if (getConnectionBackoffStrategy().shouldBackoff(out)) {
+ getBackoffManager().backOff(route);
+ } else {
+ getBackoffManager().probe(route);
+ }
+ return out;
} catch(HttpException httpException) {
throw new ClientProtocolException(httpException);
}
@@ -851,7 +915,7 @@ public abstract class AbstractHttpClient
stateHandler,
params);
}
-
+
/**
* @since 4.1
*/
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/Clock.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/Clock.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/Clock.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/Clock.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,17 @@
+package org.apache.http.impl.client;
+
+/**
+ * Interface used to enable easier testing of time-related behavior.
+ *
+ * @since 4.2
+ *
+ */
+public interface Clock {
+
+ /**
+ * Returns the current time, expressed as the number of
+ * milliseconds since the epoch.
+ * @return current time
+ */
+ long getCurrentTime();
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/Clock.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,28 @@
+package org.apache.http.impl.client;
+
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ConnectionBackoffStrategy;
+
+/**
+ * This {@link ConnectionBackoffStrategy} backs off either for a raw
+ * network socket or connection timeout or if the server explicitly
+ * sends a 503 (Service Unavailable) response.
+ *
+ * @since 4.2
+ */
+public class DefaultBackoffStrategy implements ConnectionBackoffStrategy {
+
+ public boolean shouldBackoff(Throwable t) {
+ return (t instanceof SocketTimeoutException
+ || t instanceof ConnectException);
+ }
+
+ public boolean shouldBackoff(HttpResponse resp) {
+ return (resp.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE);
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,21 @@
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ConnectionBackoffStrategy;
+
+/**
+ * This is a {@link ConnectionBackoffStrategy} that never backs off,
+ * for compatibility with existing behavior.
+ *
+ * @since 4.2
+ */
+public class NullBackoffStrategy implements ConnectionBackoffStrategy {
+
+ public boolean shouldBackoff(Throwable t) {
+ return false;
+ }
+
+ public boolean shouldBackoff(HttpResponse resp) {
+ return false;
+ }
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,14 @@
+package org.apache.http.impl.client;
+
+/**
+ * The actual system clock.
+ *
+ * @since 4.2
+ */
+public class SystemClock implements Clock {
+
+ public long getCurrentTime() {
+ return System.currentTimeMillis();
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java?rev=1147947&r1=1147946&r2=1147947&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java Mon Jul 18 16:13:44 2011
@@ -450,7 +450,7 @@ public class ConnPoolByRoute extends Abs
RouteSpecificPool rospl = getRoutePool(route, true);
- if (reusable) {
+ if (reusable && rospl.getCapacity() >= 0) {
if (log.isDebugEnabled()) {
String s;
if (validDuration > 0) {
Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java?rev=1147947&r1=1147946&r2=1147947&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java Mon Jul 18 16:13:44 2011
@@ -109,16 +109,33 @@ public class ThreadSafeClientConnManager
*/
public ThreadSafeClientConnManager(final SchemeRegistry schreg,
long connTTL, TimeUnit connTTLTimeUnit) {
+ this(schreg, connTTL, connTTLTimeUnit, new ConnPerRouteBean());
+ }
+
+ /**
+ * 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
+ * @param connPerRoute mapping of maximum connections per route,
+ * provided as a dependency so it can be managed externally, e.g.
+ * for dynamic connection pool size management.
+ *
+ * @since 4.2
+ */
+ public ThreadSafeClientConnManager(final SchemeRegistry schreg,
+ long connTTL, TimeUnit connTTLTimeUnit, ConnPerRouteBean connPerRoute) {
super();
if (schreg == null) {
throw new IllegalArgumentException("Scheme registry may not be null");
}
this.log = LogFactory.getLog(getClass());
this.schemeRegistry = schreg;
- this.connPerRoute = new ConnPerRouteBean();
+ this.connPerRoute = connPerRoute;
this.connOperator = createConnectionOperator(schreg);
this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ;
- this.connectionPool = this.pool;
+ this.connectionPool = this.pool;
}
/**
Added: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,15 @@
+package org.apache.http.impl.client;
+
+public class MockClock implements Clock {
+
+ private long t = System.currentTimeMillis();
+
+ public long getCurrentTime() {
+ return t;
+ }
+
+ public void setCurrentTime(long now) {
+ t = now;
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,151 @@
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+
+import java.util.Random;
+
+import org.apache.http.HttpHost;
+import org.apache.http.client.BackoffManager;
+import org.apache.http.conn.params.ConnPerRouteBean;
+import org.apache.http.conn.routing.HttpRoute;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestAIMDBackoffManager {
+
+ private AIMDBackoffManager impl;
+ private ConnPerRouteBean connPerRoute;
+ private HttpRoute route;
+ private MockClock clock;
+
+ @Before
+ public void setUp() {
+ connPerRoute = new ConnPerRouteBean();
+ route = new HttpRoute(new HttpHost("localhost:80"));
+ clock = new MockClock();
+ impl = new AIMDBackoffManager(connPerRoute, clock);
+ impl.setPerHostConnectionCap(10);
+ }
+
+ @Test
+ public void isABackoffManager() {
+ assertTrue(impl instanceof BackoffManager);
+ }
+
+ @Test
+ public void halvesConnectionsOnBackoff() {
+ connPerRoute.setMaxForRoute(route, 4);
+ impl.backOff(route);
+ assertEquals(2, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void doesNotBackoffBelowOneConnection() {
+ connPerRoute.setMaxForRoute(route, 1);
+ impl.backOff(route);
+ assertEquals(1, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void increasesByOneOnProbe() {
+ connPerRoute.setMaxForRoute(route, 2);
+ impl.probe(route);
+ assertEquals(3, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void doesNotIncreaseBeyondPerHostMaxOnProbe() {
+ connPerRoute.setDefaultMaxPerRoute(5);
+ connPerRoute.setMaxForRoute(route, 5);
+ impl.setPerHostConnectionCap(5);
+ impl.probe(route);
+ assertEquals(5, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void backoffDoesNotAdjustDuringCoolDownPeriod() {
+ connPerRoute.setMaxForRoute(route, 4);
+ long now = System.currentTimeMillis();
+ clock.setCurrentTime(now);
+ impl.backOff(route);
+ long max = connPerRoute.getMaxForRoute(route);
+ clock.setCurrentTime(now + 1);
+ impl.backOff(route);
+ assertEquals(max, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void backoffStillAdjustsAfterCoolDownPeriod() {
+ connPerRoute.setMaxForRoute(route, 8);
+ long now = System.currentTimeMillis();
+ clock.setCurrentTime(now);
+ impl.backOff(route);
+ long max = connPerRoute.getMaxForRoute(route);
+ clock.setCurrentTime(now + 10 * 1000L);
+ impl.backOff(route);
+ assertTrue(max == 1 || max > connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void probeDoesNotAdjustDuringCooldownPeriod() {
+ connPerRoute.setMaxForRoute(route, 4);
+ long now = System.currentTimeMillis();
+ clock.setCurrentTime(now);
+ impl.probe(route);
+ long max = connPerRoute.getMaxForRoute(route);
+ clock.setCurrentTime(now + 1);
+ impl.probe(route);
+ assertEquals(max, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void probeStillAdjustsAfterCoolDownPeriod() {
+ connPerRoute.setMaxForRoute(route, 8);
+ long now = System.currentTimeMillis();
+ clock.setCurrentTime(now);
+ impl.probe(route);
+ long max = connPerRoute.getMaxForRoute(route);
+ clock.setCurrentTime(now + 10 * 1000L);
+ impl.probe(route);
+ assertTrue(max < connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void willBackoffImmediatelyEvenAfterAProbe() {
+ connPerRoute.setMaxForRoute(route, 8);
+ long now = System.currentTimeMillis();
+ clock.setCurrentTime(now);
+ impl.probe(route);
+ long max = connPerRoute.getMaxForRoute(route);
+ clock.setCurrentTime(now + 1);
+ impl.backOff(route);
+ assertTrue(connPerRoute.getMaxForRoute(route) < max);
+ }
+
+ @Test
+ public void backOffFactorIsConfigurable() {
+ connPerRoute.setMaxForRoute(route, 10);
+ impl.setBackoffFactor(0.9);
+ impl.backOff(route);
+ assertEquals(9, connPerRoute.getMaxForRoute(route));
+ }
+
+ @Test
+ public void coolDownPeriodIsConfigurable() {
+ long cd = new Random().nextLong() / 2;
+ if (cd < 0) cd *= -1;
+ if (cd < 1) cd++;
+ long now = System.currentTimeMillis();
+ impl.setCooldownMillis(cd);
+ clock.setCurrentTime(now);
+ impl.probe(route);
+ int max0 = connPerRoute.getMaxForRoute(route);
+ clock.setCurrentTime(now);
+ impl.probe(route);
+ assertEquals(max0, connPerRoute.getMaxForRoute(route));
+ clock.setCurrentTime(now + cd + 1);
+ impl.probe(route);
+ assertTrue(max0 < connPerRoute.getMaxForRoute(route));
+ }
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,63 @@
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.ConnectionBackoffStrategy;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestDefaultBackoffStrategy {
+
+ private DefaultBackoffStrategy impl;
+
+ @Before
+ public void setUp() {
+ impl = new DefaultBackoffStrategy();
+ }
+
+ @Test
+ public void isABackoffStrategy() {
+ assertTrue(impl instanceof ConnectionBackoffStrategy);
+ }
+
+ @Test
+ public void backsOffForSocketTimeouts() {
+ assertTrue(impl.shouldBackoff(new SocketTimeoutException()));
+ }
+
+ @Test
+ public void backsOffForConnectionTimeouts() {
+ assertTrue(impl.shouldBackoff(new ConnectException()));
+ }
+
+ @Test
+ public void doesNotBackOffForConnectionManagerTimeout() {
+ assertFalse(impl.shouldBackoff(new ConnectionPoolTimeoutException()));
+ }
+
+ @Test
+ public void backsOffForServiceUnavailable() {
+ HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+ HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+ assertTrue(impl.shouldBackoff(resp));
+ }
+
+ @Test
+ public void doesNotBackOffForNon503StatusCodes() {
+ for(int i = 100; i <= 599; i++) {
+ if (i == HttpStatus.SC_SERVICE_UNAVAILABLE) continue;
+ HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+ i, "Foo");
+ assertFalse(impl.shouldBackoff(resp));
+ }
+ }
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java?rev=1147947&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java Mon Jul 18 16:13:44 2011
@@ -0,0 +1,33 @@
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestNullBackoffStrategy {
+
+ private NullBackoffStrategy impl;
+
+ @Before
+ public void setUp() {
+ impl = new NullBackoffStrategy();
+ }
+
+ @Test
+ public void doesNotBackoffForThrowables() {
+ assertFalse(impl.shouldBackoff(new Exception()));
+ }
+
+ @Test
+ public void doesNotBackoffForResponses() {
+ HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+ HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+ assertFalse(impl.shouldBackoff(resp));
+ }
+}
Propchange: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java
------------------------------------------------------------------------------
svn:eol-style = native