001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027package org.apache.http.impl.client; 028 029import java.util.HashMap; 030import java.util.Map; 031 032import org.apache.http.client.BackoffManager; 033import org.apache.http.conn.routing.HttpRoute; 034import org.apache.http.pool.ConnPoolControl; 035import org.apache.http.util.Args; 036 037/** 038 * <p>The {@code AIMDBackoffManager} applies an additive increase, 039 * multiplicative decrease (AIMD) to managing a dynamic limit to 040 * the number of connections allowed to a given host. You may want 041 * to experiment with the settings for the cooldown periods and the 042 * backoff factor to get the adaptive behavior you want.</p> 043 * 044 * <p>Generally speaking, shorter cooldowns will lead to more steady-state 045 * variability but faster reaction times, while longer cooldowns 046 * will lead to more stable equilibrium behavior but slower reaction 047 * times.</p> 048 * 049 * <p>Similarly, higher backoff factors promote greater 050 * utilization of available capacity at the expense of fairness 051 * among clients. Lower backoff factors allow equal distribution of 052 * capacity among clients (fairness) to happen faster, at the 053 * expense of having more server capacity unused in the short term.</p> 054 * 055 * @since 4.2 056 */ 057public class AIMDBackoffManager implements BackoffManager { 058 059 private final ConnPoolControl<HttpRoute> connPerRoute; 060 private final Clock clock; 061 private final Map<HttpRoute,Long> lastRouteProbes; 062 private final Map<HttpRoute,Long> lastRouteBackoffs; 063 private long coolDown = 5 * 1000L; 064 private double backoffFactor = 0.5; 065 private int cap = 2; // Per RFC 2616 sec 8.1.4 066 067 /** 068 * Creates an {@code AIMDBackoffManager} to manage 069 * per-host connection pool sizes represented by the 070 * given {@link ConnPoolControl}. 071 * @param connPerRoute per-host routing maximums to 072 * be managed 073 */ 074 public AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute) { 075 this(connPerRoute, new SystemClock()); 076 } 077 078 AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute, final Clock clock) { 079 this.clock = clock; 080 this.connPerRoute = connPerRoute; 081 this.lastRouteProbes = new HashMap<HttpRoute,Long>(); 082 this.lastRouteBackoffs = new HashMap<HttpRoute,Long>(); 083 } 084 085 @Override 086 public void backOff(final HttpRoute route) { 087 synchronized(connPerRoute) { 088 final int curr = connPerRoute.getMaxPerRoute(route); 089 final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route); 090 final long now = clock.getCurrentTime(); 091 if (now - lastUpdate.longValue() < coolDown) { 092 return; 093 } 094 connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr)); 095 lastRouteBackoffs.put(route, Long.valueOf(now)); 096 } 097 } 098 099 private int getBackedOffPoolSize(final int curr) { 100 if (curr <= 1) { 101 return 1; 102 } 103 return (int)(Math.floor(backoffFactor * curr)); 104 } 105 106 @Override 107 public void probe(final HttpRoute route) { 108 synchronized(connPerRoute) { 109 final int curr = connPerRoute.getMaxPerRoute(route); 110 final int max = (curr >= cap) ? cap : curr + 1; 111 final Long lastProbe = getLastUpdate(lastRouteProbes, route); 112 final Long lastBackoff = getLastUpdate(lastRouteBackoffs, route); 113 final long now = clock.getCurrentTime(); 114 if (now - lastProbe.longValue() < coolDown || now - lastBackoff.longValue() < coolDown) { 115 return; 116 } 117 connPerRoute.setMaxPerRoute(route, max); 118 lastRouteProbes.put(route, Long.valueOf(now)); 119 } 120 } 121 122 private Long getLastUpdate(final Map<HttpRoute,Long> updates, final HttpRoute route) { 123 Long lastUpdate = updates.get(route); 124 if (lastUpdate == null) { 125 lastUpdate = Long.valueOf(0L); 126 } 127 return lastUpdate; 128 } 129 130 /** 131 * Sets the factor to use when backing off; the new 132 * per-host limit will be roughly the current max times 133 * this factor. {@code Math.floor} is applied in the 134 * case of non-integer outcomes to ensure we actually 135 * decrease the pool size. Pool sizes are never decreased 136 * below 1, however. Defaults to 0.5. 137 * @param d must be between 0.0 and 1.0, exclusive. 138 */ 139 public void setBackoffFactor(final double d) { 140 Args.check(d > 0.0 && d < 1.0, "Backoff factor must be 0.0 < f < 1.0"); 141 backoffFactor = d; 142 } 143 144 /** 145 * Sets the amount of time, in milliseconds, to wait between 146 * adjustments in pool sizes for a given host, to allow 147 * enough time for the adjustments to take effect. Defaults 148 * to 5000L (5 seconds). 149 * @param l must be positive 150 */ 151 public void setCooldownMillis(final long l) { 152 Args.positive(coolDown, "Cool down"); 153 coolDown = l; 154 } 155 156 /** 157 * Sets the absolute maximum per-host connection pool size to 158 * probe up to; defaults to 2 (the default per-host max). 159 * @param cap must be >= 1 160 */ 161 public void setPerHostConnectionCap(final int cap) { 162 Args.positive(cap, "Per host connection cap"); 163 this.cap = cap; 164 } 165 166}