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.hc.client5.http.impl.classic;
28
29 import java.util.HashMap;
30 import java.util.Map;
31
32 import org.apache.hc.client5.http.HttpRoute;
33 import org.apache.hc.client5.http.classic.BackoffManager;
34 import org.apache.hc.core5.annotation.Experimental;
35 import org.apache.hc.core5.pool.ConnPoolControl;
36 import org.apache.hc.core5.util.Args;
37 import org.apache.hc.core5.util.TimeValue;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 @Experimental
60 public class AIMDBackoffManager implements BackoffManager {
61
62 private final ConnPoolControl<HttpRoute> connPerRoute;
63 private final Clock clock;
64 private final Map<HttpRoute, Long> lastRouteProbes;
65 private final Map<HttpRoute, Long> lastRouteBackoffs;
66 private TimeValue coolDown = TimeValue.ofSeconds(5L);
67 private double backoffFactor = 0.5;
68 private int cap = 2;
69
70
71
72
73
74
75
76
77 public AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute) {
78 this(connPerRoute, new SystemClock());
79 }
80
81 AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute, final Clock clock) {
82 this.clock = clock;
83 this.connPerRoute = connPerRoute;
84 this.lastRouteProbes = new HashMap<>();
85 this.lastRouteBackoffs = new HashMap<>();
86 }
87
88 @Override
89 public void backOff(final HttpRoute route) {
90 synchronized(connPerRoute) {
91 final int curr = connPerRoute.getMaxPerRoute(route);
92 final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route);
93 final long now = clock.getCurrentTime();
94 if (now - lastUpdate < coolDown.toMilliseconds()) {
95 return;
96 }
97 connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr));
98 lastRouteBackoffs.put(route, now);
99 }
100 }
101
102 private int getBackedOffPoolSize(final int curr) {
103 if (curr <= 1) {
104 return 1;
105 }
106 return (int)(Math.floor(backoffFactor * curr));
107 }
108
109 @Override
110 public void probe(final HttpRoute route) {
111 synchronized(connPerRoute) {
112 final int curr = connPerRoute.getMaxPerRoute(route);
113 final int max = (curr >= cap) ? cap : curr + 1;
114 final Long lastProbe = getLastUpdate(lastRouteProbes, route);
115 final Long lastBackoff = getLastUpdate(lastRouteBackoffs, route);
116 final long now = clock.getCurrentTime();
117 if (now - lastProbe < coolDown.toMilliseconds()
118 || now - lastBackoff < coolDown.toMilliseconds()) {
119 return;
120 }
121 connPerRoute.setMaxPerRoute(route, max);
122 lastRouteProbes.put(route, now);
123 }
124 }
125
126 private Long getLastUpdate(final Map<HttpRoute, Long> updates, final HttpRoute route) {
127 Long lastUpdate = updates.get(route);
128 if (lastUpdate == null) {
129 lastUpdate = 0L;
130 }
131 return lastUpdate;
132 }
133
134
135
136
137
138
139
140
141
142
143 public void setBackoffFactor(final double d) {
144 Args.check(d > 0.0 && d < 1.0, "Backoff factor must be 0.0 < f < 1.0");
145 backoffFactor = d;
146 }
147
148
149
150
151
152
153
154 public void setCoolDown(final TimeValue coolDown) {
155 Args.positive(coolDown.getDuration(), "coolDown");
156 this.coolDown = coolDown;
157 }
158
159
160
161
162
163
164 public void setPerHostConnectionCap(final int cap) {
165 Args.positive(cap, "Per host connection cap");
166 this.cap = cap;
167 }
168
169 }