1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 package ch.boye.httpclientandroidlib.impl.client; 28 29 import java.util.HashMap; 30 import java.util.Map; 31 32 import ch.boye.httpclientandroidlib.client.BackoffManager; 33 import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; 34 import ch.boye.httpclientandroidlib.pool.ConnPoolControl; 35 import ch.boye.httpclientandroidlib.util.Args; 36 37 /** 38 * <p>The <code>AIMDBackoffManager</code> applies an additive increase, 39 * multiplicative decrease (AIMD) to managing a dynamic limit to 40 * the number of connections allowed to a given host. You may want 41 * to experiment with the settings for the cooldown periods and the 42 * backoff factor to get the adaptive behavior you want.</p> 43 * 44 * <p>Generally speaking, shorter cooldowns will lead to more steady-state 45 * variability but faster reaction times, while longer cooldowns 46 * will lead to more stable equilibrium behavior but slower reaction 47 * times.</p> 48 * 49 * <p>Similarly, higher backoff factors promote greater 50 * utilization of available capacity at the expense of fairness 51 * among clients. Lower backoff factors allow equal distribution of 52 * capacity among clients (fairness) to happen faster, at the 53 * expense of having more server capacity unused in the short term.</p> 54 * 55 * @since 4.2 56 */ 57 public class AIMDBackoffManager implements BackoffManager { 58 59 private final ConnPoolControl<HttpRoute> connPerRoute; 60 private final Clock clock; 61 private final Map<HttpRoute,Long> lastRouteProbes; 62 private final Map<HttpRoute,Long> lastRouteBackoffs; 63 private long coolDown = 5 * 1000L; 64 private double backoffFactor = 0.5; 65 private int cap = 2; // Per RFC 2616 sec 8.1.4 66 67 /** 68 * Creates an <code>AIMDBackoffManager</code> to manage 69 * per-host connection pool sizes represented by the 70 * given {@link ConnPoolControl}. 71 * @param connPerRoute per-host routing maximums to 72 * be managed 73 */ AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute)74 public AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute) { 75 this(connPerRoute, new SystemClock()); 76 } 77 AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute, final Clock clock)78 AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute, final Clock clock) { 79 this.clock = clock; 80 this.connPerRoute = connPerRoute; 81 this.lastRouteProbes = new HashMap<HttpRoute,Long>(); 82 this.lastRouteBackoffs = new HashMap<HttpRoute,Long>(); 83 } 84 backOff(final HttpRoute route)85 public void backOff(final HttpRoute route) { 86 synchronized(connPerRoute) { 87 final int curr = connPerRoute.getMaxPerRoute(route); 88 final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route); 89 final long now = clock.getCurrentTime(); 90 if (now - lastUpdate.longValue() < coolDown) { 91 return; 92 } 93 connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr)); 94 lastRouteBackoffs.put(route, Long.valueOf(now)); 95 } 96 } 97 getBackedOffPoolSize(final int curr)98 private int getBackedOffPoolSize(final int curr) { 99 if (curr <= 1) { 100 return 1; 101 } 102 return (int)(Math.floor(backoffFactor * curr)); 103 } 104 probe(final HttpRoute route)105 public void probe(final HttpRoute route) { 106 synchronized(connPerRoute) { 107 final int curr = connPerRoute.getMaxPerRoute(route); 108 final int max = (curr >= cap) ? cap : curr + 1; 109 final Long lastProbe = getLastUpdate(lastRouteProbes, route); 110 final Long lastBackoff = getLastUpdate(lastRouteBackoffs, route); 111 final long now = clock.getCurrentTime(); 112 if (now - lastProbe.longValue() < coolDown || now - lastBackoff.longValue() < coolDown) { 113 return; 114 } 115 connPerRoute.setMaxPerRoute(route, max); 116 lastRouteProbes.put(route, Long.valueOf(now)); 117 } 118 } 119 getLastUpdate(final Map<HttpRoute,Long> updates, final HttpRoute route)120 private Long getLastUpdate(final Map<HttpRoute,Long> updates, final HttpRoute route) { 121 Long lastUpdate = updates.get(route); 122 if (lastUpdate == null) { 123 lastUpdate = Long.valueOf(0L); 124 } 125 return lastUpdate; 126 } 127 128 /** 129 * Sets the factor to use when backing off; the new 130 * per-host limit will be roughly the current max times 131 * this factor. <code>Math.floor</code> is applied in the 132 * case of non-integer outcomes to ensure we actually 133 * decrease the pool size. Pool sizes are never decreased 134 * below 1, however. Defaults to 0.5. 135 * @param d must be between 0.0 and 1.0, exclusive. 136 */ setBackoffFactor(final double d)137 public void setBackoffFactor(final double d) { 138 Args.check(d > 0.0 && d < 1.0, "Backoff factor must be 0.0 < f < 1.0"); 139 backoffFactor = d; 140 } 141 142 /** 143 * Sets the amount of time, in milliseconds, to wait between 144 * adjustments in pool sizes for a given host, to allow 145 * enough time for the adjustments to take effect. Defaults 146 * to 5000L (5 seconds). 147 * @param l must be positive 148 */ setCooldownMillis(final long l)149 public void setCooldownMillis(final long l) { 150 Args.positive(coolDown, "Cool down"); 151 coolDown = l; 152 } 153 154 /** 155 * Sets the absolute maximum per-host connection pool size to 156 * probe up to; defaults to 2 (the default per-host max). 157 * @param cap must be >= 1 158 */ setPerHostConnectionCap(final int cap)159 public void setPerHostConnectionCap(final int cap) { 160 Args.positive(cap, "Per host connection cap"); 161 this.cap = cap; 162 } 163 164 } 165