Skip to content

Commit f6de60d

Browse files
bsideupkdvolder
andauthored
Make max connections configurable (#1501)
* Allow configuring http connection pool size (#1474) See: #1466 * Add a test, set default to max * remove unused class Co-authored-by: Kris De Volder <kdevolder@pivotal.io>
1 parent d85ea79 commit f6de60d

File tree

7 files changed

+120
-36
lines changed

7 files changed

+120
-36
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
.project
77
.settings
88
.classpath
9+
.factorypath
910

1011
# Ignore all build/dist directories
1112
target

docker-java-transport-httpclient5/src/main/java/com/github/dockerjava/httpclient5/ApacheDockerHttpClient.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public static final class Builder {
1313

1414
private SSLConfig sslConfig = null;
1515

16+
private int maxConnections = Integer.MAX_VALUE;
17+
1618
public Builder dockerHost(URI value) {
1719
this.dockerHost = Objects.requireNonNull(value, "dockerHost");
1820
return this;
@@ -23,13 +25,18 @@ public Builder sslConfig(SSLConfig value) {
2325
return this;
2426
}
2527

28+
public Builder maxConnections(int value) {
29+
this.maxConnections = value;
30+
return this;
31+
}
32+
2633
public ApacheDockerHttpClient build() {
2734
Objects.requireNonNull(dockerHost, "dockerHost");
28-
return new ApacheDockerHttpClient(dockerHost, sslConfig);
35+
return new ApacheDockerHttpClient(dockerHost, sslConfig, maxConnections);
2936
}
3037
}
3138

32-
private ApacheDockerHttpClient(URI dockerHost, SSLConfig sslConfig) {
33-
super(dockerHost, sslConfig);
39+
private ApacheDockerHttpClient(URI dockerHost, SSLConfig sslConfig, int maxConnections) {
40+
super(dockerHost, sslConfig, maxConnections);
3441
}
3542
}

docker-java-transport-httpclient5/src/main/java/com/github/dockerjava/httpclient5/ApacheDockerHttpClientImpl.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@
4141
class ApacheDockerHttpClientImpl implements DockerHttpClient {
4242

4343
private final CloseableHttpClient httpClient;
44-
4544
private final HttpHost host;
4645

4746
protected ApacheDockerHttpClientImpl(
4847
URI dockerHost,
49-
SSLConfig sslConfig
48+
SSLConfig sslConfig,
49+
int maxConnections
5050
) {
5151
Registry<ConnectionSocketFactory> socketFactoryRegistry = createConnectionSocketFactoryRegistry(sslConfig, dockerHost);
5252

@@ -66,27 +66,30 @@ protected ApacheDockerHttpClientImpl(
6666
host = HttpHost.create(dockerHost);
6767
}
6868

69+
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
70+
socketFactoryRegistry,
71+
new ManagedHttpClientConnectionFactory(
72+
null,
73+
null,
74+
null,
75+
null,
76+
message -> {
77+
Header transferEncodingHeader = message.getFirstHeader(HttpHeaders.TRANSFER_ENCODING);
78+
if (transferEncodingHeader != null) {
79+
if ("identity".equalsIgnoreCase(transferEncodingHeader.getValue())) {
80+
return ContentLengthStrategy.UNDEFINED;
81+
}
82+
}
83+
return DefaultContentLengthStrategy.INSTANCE.determineLength(message);
84+
},
85+
null
86+
)
87+
);
88+
connectionManager.setMaxTotal(maxConnections);
89+
connectionManager.setDefaultMaxPerRoute(maxConnections);
6990
httpClient = HttpClients.custom()
7091
.setRequestExecutor(new HijackingHttpRequestExecutor(null))
71-
.setConnectionManager(new PoolingHttpClientConnectionManager(
72-
socketFactoryRegistry,
73-
new ManagedHttpClientConnectionFactory(
74-
null,
75-
null,
76-
null,
77-
null,
78-
message -> {
79-
Header transferEncodingHeader = message.getFirstHeader(HttpHeaders.TRANSFER_ENCODING);
80-
if (transferEncodingHeader != null) {
81-
if ("identity".equalsIgnoreCase(transferEncodingHeader.getValue())) {
82-
return ContentLengthStrategy.UNDEFINED;
83-
}
84-
}
85-
return DefaultContentLengthStrategy.INSTANCE.determineLength(message);
86-
},
87-
null
88-
)
89-
))
92+
.setConnectionManager(connectionManager)
9093
.build();
9194
}
9295

docker-java-transport-jersey/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerHttpClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ public static final class Builder {
5454

5555
private Integer connectTimeout = null;
5656

57-
private Integer maxTotalConnections = null;
57+
private Integer maxTotalConnections = Integer.MAX_VALUE;
5858

59-
private Integer maxPerRouteConnections = null;
59+
private Integer maxPerRouteConnections = Integer.MAX_VALUE;
6060

6161
private Integer connectionRequestTimeout = null;
6262

docker-java-transport-zerodep/src/main/java/com/github/dockerjava/httpclient5/ZerodepDockerHttpClient.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public static final class Builder {
1414

1515
private SSLConfig sslConfig = null;
1616

17+
private int maxConnections = Integer.MAX_VALUE;
18+
1719
public Builder dockerHost(URI value) {
1820
this.dockerHost = Objects.requireNonNull(value, "dockerHost");
1921
return this;
@@ -24,13 +26,18 @@ public Builder sslConfig(SSLConfig value) {
2426
return this;
2527
}
2628

29+
public Builder maxConnections(int value) {
30+
this.maxConnections = value;
31+
return this;
32+
}
33+
2734
public ZerodepDockerHttpClient build() {
2835
Objects.requireNonNull(dockerHost, "dockerHost");
29-
return new ZerodepDockerHttpClient(dockerHost, sslConfig);
36+
return new ZerodepDockerHttpClient(dockerHost, sslConfig, maxConnections);
3037
}
3138
}
3239

33-
protected ZerodepDockerHttpClient(URI dockerHost, SSLConfig sslConfig) {
34-
super(dockerHost, sslConfig);
40+
protected ZerodepDockerHttpClient(URI dockerHost, SSLConfig sslConfig, int maxConnections) {
41+
super(dockerHost, sslConfig, maxConnections);
3542
}
3643
}

docker-java/src/test/java/com/github/dockerjava/cmd/LogContainerCmdIT.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
package com.github.dockerjava.cmd;
22

3+
import com.github.dockerjava.api.DockerClient;
4+
import com.github.dockerjava.api.async.ResultCallback;
35
import com.github.dockerjava.api.command.CreateContainerResponse;
46
import com.github.dockerjava.api.exception.NotFoundException;
7+
import com.github.dockerjava.api.model.Frame;
58
import com.github.dockerjava.api.model.StreamType;
69
import com.github.dockerjava.utils.LogContainerTestCallback;
710
import org.junit.Test;
811
import org.slf4j.Logger;
912
import org.slf4j.LoggerFactory;
1013

1114
import java.io.IOException;
15+
import java.util.List;
16+
import java.util.concurrent.Callable;
17+
import java.util.concurrent.CopyOnWriteArrayList;
18+
import java.util.concurrent.ExecutorService;
19+
import java.util.concurrent.Executors;
1220
import java.util.concurrent.TimeUnit;
21+
import java.util.concurrent.atomic.AtomicBoolean;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.LongStream;
1324

25+
import static org.awaitility.Awaitility.await;
26+
import static org.hamcrest.CoreMatchers.everyItem;
1427
import static org.hamcrest.CoreMatchers.is;
1528
import static org.hamcrest.MatcherAssert.assertThat;
1629
import static org.hamcrest.Matchers.containsString;
17-
import static org.hamcrest.Matchers.equalTo;
1830
import static org.hamcrest.Matchers.emptyString;
31+
import static org.hamcrest.Matchers.equalTo;
32+
import static org.hamcrest.Matchers.hasSize;
33+
import static org.hamcrest.Matchers.hasToString;
1934
import static org.hamcrest.Matchers.not;
2035
import static org.junit.Assert.assertEquals;
2136
import static org.junit.Assert.assertTrue;
@@ -197,4 +212,52 @@ public void asyncLogContainerWithSince() throws Exception {
197212

198213
assertThat(loggingCallback.toString(), containsString(snippet));
199214
}
215+
216+
@Test(timeout = 10_000)
217+
public void simultaneousCommands() throws Exception {
218+
// Create a new client to not affect other tests
219+
DockerClient client = dockerRule.newClient();
220+
CreateContainerResponse container = client.createContainerCmd("busybox")
221+
.withCmd("/bin/sh", "-c", "echo hello world; sleep infinity")
222+
.exec();
223+
224+
client.startContainerCmd(container.getId()).exec();
225+
226+
// Simulate 100 simultaneous connections
227+
int connections = 100;
228+
229+
ExecutorService executor = Executors.newFixedThreadPool(connections);
230+
try {
231+
List<Frame> firstFrames = new CopyOnWriteArrayList<>();
232+
executor.invokeAll(
233+
LongStream.range(0, connections).<Callable<Object>>mapToObj(__ -> {
234+
return () -> {
235+
return client.logContainerCmd(container.getId())
236+
.withStdOut(true)
237+
.withFollowStream(true)
238+
.exec(new ResultCallback.Adapter<Frame>() {
239+
240+
final AtomicBoolean first = new AtomicBoolean(true);
241+
242+
@Override
243+
public void onNext(Frame object) {
244+
if (first.compareAndSet(true, false)) {
245+
firstFrames.add(object);
246+
}
247+
super.onNext(object);
248+
}
249+
});
250+
};
251+
}).collect(Collectors.toList())
252+
);
253+
254+
await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
255+
assertThat(firstFrames, hasSize(connections));
256+
});
257+
258+
assertThat(firstFrames, everyItem(hasToString("STDOUT: hello world")));
259+
} finally {
260+
executor.shutdownNow();
261+
}
262+
}
200263
}

docker-java/src/test/java/com/github/dockerjava/core/DockerRule.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ public DockerRule(CmdIT cmdIT) {
4646
}
4747

4848

49-
public DockerClient getClient() {
50-
if (dockerClient != null) {
51-
return dockerClient;
52-
}
53-
49+
public DockerClient newClient() {
5450
DockerClientImpl dockerClient = cmdIT.getFactoryType().createDockerClient(config());
5551
DockerHttpClient dockerHttpClient = dockerClient.getHttpClient();
5652

@@ -88,14 +84,21 @@ public CreateVolumeCmd.Exec createCreateVolumeCmdExec() {
8884
}
8985
);
9086

91-
return this.dockerClient = new DockerClientDelegate(dockerClient) {
87+
return new DockerClientDelegate(dockerClient) {
9288
@Override
9389
public DockerHttpClient getHttpClient() {
9490
return dockerHttpClient;
9591
}
9692
};
9793
}
9894

95+
public DockerClient getClient() {
96+
if (dockerClient != null) {
97+
return dockerClient;
98+
}
99+
return this.dockerClient = newClient();
100+
}
101+
99102
@Override
100103
public Statement apply(Statement base, Description description) {
101104
return super.apply(base, description);

0 commit comments

Comments
 (0)