1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.http.impl.client;
28
29 import java.util.HashMap;
30 import java.util.Map;
31
32 import org.apache.http.client.BackoffManager;
33 import org.apache.http.conn.routing.HttpRoute;
34 import org.apache.http.pool.ConnPoolControl;
35 import org.apache.http.util.Args;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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;
66
67
68
69
70
71
72
73
74 public AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute) {
75 this(connPerRoute, new SystemClock());
76 }
77
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
85 @Override
86 public void backOff(final HttpRoute route) {
87 synchronized(connPerRoute) {
88 final int curr = connPerRoute.getMaxPerRoute(route);
89 final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route);
90 final long now = clock.getCurrentTime();
91 if (now - lastUpdate.longValue() < coolDown) {
92 return;
93 }
94 connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr));
95 lastRouteBackoffs.put(route, Long.valueOf(now));
96 }
97 }
98
99 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
132
133
134
135
136
137
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
146
147
148
149
150
151 public void setCooldownMillis(final long l) {
152 Args.positive(coolDown, "Cool down");
153 coolDown = l;
154 }
155
156
157
158
159
160
161 public void setPerHostConnectionCap(final int cap) {
162 Args.positive(cap, "Per host connection cap");
163 this.cap = cap;
164 }
165
166 }