Skip to content

Commit cd6caa1

Browse files
committed
PR comments addressed, tests fixed
1 parent b641b46 commit cd6caa1

File tree

11 files changed

+300
-1
lines changed

11 files changed

+300
-1
lines changed

client/src/main/java/org/asynchttpclient/AsyncHttpClient.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,11 @@ public interface AsyncHttpClient extends Closeable {
266266
* @return a {@link Future} of type Response
267267
*/
268268
ListenableFuture<Response> executeRequest(RequestBuilder requestBuilder);
269+
270+
/***
271+
* Return details about pooled connections.
272+
*
273+
* @return a {@link ClientStats}
274+
*/
275+
ClientStats getClientStats();
269276
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2010 Ning, Inc.
3+
*
4+
* This program is licensed to you under the Apache License, version 2.0
5+
* (the "License"); you may not use this file except in compliance with the
6+
* License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*
16+
*/
17+
package org.asynchttpclient;
18+
19+
import java.util.Objects;
20+
21+
/**
22+
* A record class representing the state of an (@link org.asynchttpclient.AsyncHttpClient)
23+
*/
24+
public class ClientStats {
25+
26+
private final long activeConnectionCount;
27+
private final long idleConnectionCount;
28+
29+
public ClientStats(final long activeConnectionCount,
30+
final long idleConnectionCount) {
31+
this.activeConnectionCount = activeConnectionCount;
32+
this.idleConnectionCount = idleConnectionCount;
33+
}
34+
35+
/**
36+
* @return The sum of {@link #getActiveConnectionCount()} and {@link #getIdleConnectionCount()},
37+
* a long representing the total number of connections in the connection pool.
38+
*/
39+
public long getTotalConnectionCount() {
40+
return activeConnectionCount + idleConnectionCount;
41+
}
42+
43+
/**
44+
* @return A long representing the number of active connection in the connection pool.
45+
*/
46+
public long getActiveConnectionCount() {
47+
return activeConnectionCount;
48+
}
49+
50+
/**
51+
*
52+
* @return A long representing the number of idle connections in the connection pool.
53+
*/
54+
public long getIdleConnectionCount() {
55+
return idleConnectionCount;
56+
}
57+
58+
@Override
59+
public String toString() {
60+
return "There are " + getTotalConnectionCount() +
61+
" total connections, " + getActiveConnectionCount() +
62+
" are active and " + getIdleConnectionCount() + " are idle.";
63+
}
64+
65+
@Override
66+
public boolean equals(final Object o) {
67+
if (this == o) return true;
68+
if (o == null || getClass() != o.getClass()) return false;
69+
final ClientStats that = (ClientStats) o;
70+
return activeConnectionCount == that.activeConnectionCount &&
71+
idleConnectionCount == that.idleConnectionCount;
72+
}
73+
74+
@Override
75+
public int hashCode() {
76+
return Objects.hash(activeConnectionCount, idleConnectionCount);
77+
}
78+
}

client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ public EventLoopGroup getEventLoopGroup() {
255255
return channelManager.getEventLoopGroup();
256256
}
257257

258+
@Override
259+
public ClientStats getClientStats() {
260+
return channelManager.getClientStats();
261+
}
262+
258263
protected BoundRequestBuilder requestBuilder(String method, String url) {
259264
return new BoundRequestBuilder(this, method, config.isDisableUrlEncodingForBoundRequests()).setUrl(url).setSignatureCalculator(signatureCalculator);
260265
}

client/src/main/java/org/asynchttpclient/channel/ChannelPool.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,9 @@ public interface ChannelPool {
7070
* @param selector the selector
7171
*/
7272
void flushPartitions(ChannelPoolPartitionSelector selector);
73+
74+
/**
75+
* @return The number of idle channels.
76+
*/
77+
long getIdleChannelCount();
7378
}

client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,11 @@ public void flushPartition(Object partitionKey) {
5050
@Override
5151
public void flushPartitions(ChannelPoolPartitionSelector selector) {
5252
}
53+
54+
@Override
55+
public long getIdleChannelCount() {
56+
return 0;
57+
}
58+
59+
5360
}

client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
import org.asynchttpclient.AsyncHandler;
5353
import org.asynchttpclient.AsyncHttpClientConfig;
54+
import org.asynchttpclient.ClientStats;
5455
import org.asynchttpclient.SslEngineFactory;
5556
import org.asynchttpclient.channel.ChannelPool;
5657
import org.asynchttpclient.channel.ChannelPoolPartitioning;
@@ -488,4 +489,11 @@ public ChannelPool getChannelPool() {
488489
public EventLoopGroup getEventLoopGroup() {
489490
return eventLoopGroup;
490491
}
492+
493+
public ClientStats getClientStats() {
494+
final long totalConnectionCount = openChannels.size();
495+
final long idleConnectionCount = channelPool.getIdleChannelCount();
496+
final long activeConnectionCount = totalConnectionCount - idleConnectionCount;
497+
return new ClientStats(activeConnectionCount, idleConnectionCount);
498+
}
491499
}

client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ public void flushPartitions(ChannelPoolPartitionSelector selector) {
356356
}
357357
}
358358

359+
@Override
360+
public long getIdleChannelCount() {
361+
return partitions.reduceValuesToLong(
362+
Long.MAX_VALUE,
363+
ConcurrentLinkedDeque::size,
364+
0,
365+
(left, right) -> left + right
366+
);
367+
}
368+
359369
public enum PoolLeaseStrategy {
360370
LIFO {
361371
public <E> E lease(Deque<E> d) {
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package org.asynchttpclient;
2+
3+
import static org.asynchttpclient.Dsl.asyncHttpClient;
4+
import static org.asynchttpclient.Dsl.config;
5+
import static org.testng.Assert.assertEquals;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
import org.testng.annotations.Test;
11+
12+
/**
13+
* Created by grenville on 9/25/16.
14+
*/
15+
public class ClientStatsTest extends AbstractBasicTest {
16+
17+
@Test(groups = "standalone")
18+
public void testClientStatus() throws Throwable {
19+
try (final DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config().setKeepAlive(true).setPooledConnectionIdleTimeout(5000))) {
20+
final String url = getTargetUrl();
21+
22+
final ClientStats emptyStats = client.getClientStats();
23+
24+
assertEquals(emptyStats.toString(), "There are 0 total connections, 0 are active and 0 are idle.");
25+
assertEquals(emptyStats.getActiveConnectionCount(), 0);
26+
assertEquals(emptyStats.getIdleConnectionCount(), 0);
27+
assertEquals(emptyStats.getTotalConnectionCount(), 0);
28+
29+
final List<ListenableFuture<Response>> futures = new ArrayList<>();
30+
for (int i = 0; i < 5; i++) {
31+
logger.info("{} requesting url [{}]...", i, url);
32+
futures.add(client.prepareGet(url).setHeader("LockThread", "6").execute());
33+
}
34+
35+
Thread.sleep(2000);
36+
37+
final ClientStats activeStats = client.getClientStats();
38+
39+
assertEquals(activeStats.toString(), "There are 5 total connections, 5 are active and 0 are idle.");
40+
assertEquals(activeStats.getActiveConnectionCount(), 5);
41+
assertEquals(activeStats.getIdleConnectionCount(), 0);
42+
assertEquals(activeStats.getTotalConnectionCount(), 5);
43+
44+
for (final ListenableFuture<Response> future : futures) {
45+
future.get();
46+
}
47+
48+
Thread.sleep(1000);
49+
50+
final ClientStats idleStats = client.getClientStats();
51+
52+
assertEquals(idleStats.toString(), "There are 5 total connections, 0 are active and 5 are idle.");
53+
assertEquals(idleStats.getActiveConnectionCount(), 0);
54+
assertEquals(idleStats.getIdleConnectionCount(), 5);
55+
assertEquals(idleStats.getTotalConnectionCount(), 5);
56+
57+
// Let's make sure the active count is correct when reusing cached connections.
58+
59+
final List<ListenableFuture<Response>> repeatedFutures = new ArrayList<>();
60+
for (int i = 0; i < 3; i++) {
61+
logger.info("{} requesting url [{}]...", i, url);
62+
repeatedFutures.add(client.prepareGet(url).setHeader("LockThread", "6").execute());
63+
}
64+
65+
Thread.sleep(2000);
66+
67+
final ClientStats activeCachedStats = client.getClientStats();
68+
69+
assertEquals(activeCachedStats.toString(), "There are 5 total connections, 3 are active and 2 are idle.");
70+
assertEquals(activeCachedStats.getActiveConnectionCount(), 3);
71+
assertEquals(activeCachedStats.getIdleConnectionCount(), 2);
72+
assertEquals(activeCachedStats.getTotalConnectionCount(), 5);
73+
74+
for (final ListenableFuture<Response> future : repeatedFutures) {
75+
future.get();
76+
}
77+
78+
Thread.sleep(1000);
79+
80+
final ClientStats idleCachedStats = client.getClientStats();
81+
82+
assertEquals(idleCachedStats.toString(), "There are 3 total connections, 0 are active and 3 are idle.");
83+
assertEquals(idleCachedStats.getActiveConnectionCount(), 0);
84+
assertEquals(idleCachedStats.getIdleConnectionCount(), 3);
85+
assertEquals(idleCachedStats.getTotalConnectionCount(), 3);
86+
87+
Thread.sleep(5000);
88+
89+
final ClientStats timeoutStats = client.getClientStats();
90+
91+
assertEquals(timeoutStats.toString(), "There are 0 total connections, 0 are active and 0 are idle.");
92+
assertEquals(timeoutStats.getActiveConnectionCount(), 0);
93+
assertEquals(timeoutStats.getIdleConnectionCount(), 0);
94+
assertEquals(timeoutStats.getTotalConnectionCount(), 0);
95+
}
96+
}
97+
98+
@Test(groups = "standalone")
99+
public void testClientStatusNoKeepalive() throws Throwable {
100+
try (final DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config().setKeepAlive(false))) {
101+
final String url = getTargetUrl();
102+
103+
final ClientStats emptyStats = client.getClientStats();
104+
105+
assertEquals(emptyStats.toString(), "There are 0 total connections, 0 are active and 0 are idle.");
106+
assertEquals(emptyStats.getActiveConnectionCount(), 0);
107+
assertEquals(emptyStats.getIdleConnectionCount(), 0);
108+
assertEquals(emptyStats.getTotalConnectionCount(), 0);
109+
110+
final List<ListenableFuture<Response>> futures = new ArrayList<>();
111+
for (int i = 0; i < 5; i++) {
112+
logger.info("{} requesting url [{}]...", i, url);
113+
futures.add(client.prepareGet(url).setHeader("LockThread", "6").execute());
114+
}
115+
116+
Thread.sleep(2000);
117+
118+
final ClientStats activeStats = client.getClientStats();
119+
120+
assertEquals(activeStats.toString(), "There are 5 total connections, 5 are active and 0 are idle.");
121+
assertEquals(activeStats.getActiveConnectionCount(), 5);
122+
assertEquals(activeStats.getIdleConnectionCount(), 0);
123+
assertEquals(activeStats.getTotalConnectionCount(), 5);
124+
125+
for (final ListenableFuture<Response> future : futures) {
126+
future.get();
127+
}
128+
129+
Thread.sleep(1000);
130+
131+
final ClientStats idleStats = client.getClientStats();
132+
133+
assertEquals(idleStats.toString(), "There are 0 total connections, 0 are active and 0 are idle.");
134+
assertEquals(idleStats.getActiveConnectionCount(), 0);
135+
assertEquals(idleStats.getIdleConnectionCount(), 0);
136+
assertEquals(idleStats.getTotalConnectionCount(), 0);
137+
138+
// Let's make sure the active count is correct when reusing cached connections.
139+
140+
final List<ListenableFuture<Response>> repeatedFutures = new ArrayList<>();
141+
for (int i = 0; i < 3; i++) {
142+
logger.info("{} requesting url [{}]...", i, url);
143+
repeatedFutures.add(client.prepareGet(url).setHeader("LockThread", "6").execute());
144+
}
145+
146+
Thread.sleep(2000);
147+
148+
final ClientStats activeCachedStats = client.getClientStats();
149+
150+
assertEquals(activeCachedStats.toString(), "There are 3 total connections, 3 are active and 0 are idle.");
151+
assertEquals(activeCachedStats.getActiveConnectionCount(), 3);
152+
assertEquals(activeCachedStats.getIdleConnectionCount(), 0);
153+
assertEquals(activeCachedStats.getTotalConnectionCount(), 3);
154+
155+
for (final ListenableFuture<Response> future : repeatedFutures) {
156+
future.get();
157+
}
158+
159+
Thread.sleep(1000);
160+
161+
final ClientStats idleCachedStats = client.getClientStats();
162+
163+
assertEquals(idleCachedStats.toString(), "There are 0 total connections, 0 are active and 0 are idle.");
164+
assertEquals(idleCachedStats.getActiveConnectionCount(), 0);
165+
assertEquals(idleCachedStats.getIdleConnectionCount(), 0);
166+
assertEquals(idleCachedStats.getTotalConnectionCount(), 0);
167+
}
168+
}
169+
}

client/src/test/java/org/asynchttpclient/test/EchoHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ public void handle(String pathInContext, Request request, HttpServletRequest htt
5555
param = e.nextElement().toString();
5656

5757
if (param.startsWith("LockThread")) {
58+
final int sleepTime = httpRequest.getIntHeader(param);
5859
try {
59-
Thread.sleep(40 * 1000);
60+
Thread.sleep(sleepTime == -1 ? 40 : sleepTime * 1000);
6061
} catch (InterruptedException ex) {
6162
}
6263
}

extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,9 @@ public <T> ListenableFuture<T> executeRequest(RequestBuilder requestBuilder, Asy
125125
public ListenableFuture<Response> executeRequest(RequestBuilder requestBuilder) {
126126
return null;
127127
}
128+
129+
@Override
130+
public ClientStats getClientStats() {
131+
return null;
132+
}
128133
}

extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,8 @@ public ListenableFuture<Response> executeRequest(RequestBuilder requestBuilder)
122122
return null;
123123
}
124124

125+
@Override
126+
public ClientStats getClientStats() {
127+
return null;
128+
}
125129
}

0 commit comments

Comments
 (0)