From cd6caa10e7fe5bdfc8f7df3e4cafab809bd1e947 Mon Sep 17 00:00:00 2001 From: Grenville Wilson Date: Mon, 31 Oct 2016 16:43:17 -0400 Subject: [PATCH 1/4] PR comments addressed, tests fixed --- .../org/asynchttpclient/AsyncHttpClient.java | 7 + .../java/org/asynchttpclient/ClientStats.java | 78 ++++++++ .../DefaultAsyncHttpClient.java | 5 + .../asynchttpclient/channel/ChannelPool.java | 5 + .../channel/NoopChannelPool.java | 7 + .../netty/channel/ChannelManager.java | 8 + .../netty/channel/DefaultChannelPool.java | 10 ++ .../org/asynchttpclient/ClientStatsTest.java | 169 ++++++++++++++++++ .../org/asynchttpclient/test/EchoHandler.java | 3 +- .../extras/registry/BadAsyncHttpClient.java | 5 + .../extras/registry/TestAsyncHttpClient.java | 4 + 11 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 client/src/main/java/org/asynchttpclient/ClientStats.java create mode 100644 client/src/test/java/org/asynchttpclient/ClientStatsTest.java diff --git a/client/src/main/java/org/asynchttpclient/AsyncHttpClient.java b/client/src/main/java/org/asynchttpclient/AsyncHttpClient.java index 784f65b89d..c21cd59af1 100755 --- a/client/src/main/java/org/asynchttpclient/AsyncHttpClient.java +++ b/client/src/main/java/org/asynchttpclient/AsyncHttpClient.java @@ -266,4 +266,11 @@ public interface AsyncHttpClient extends Closeable { * @return a {@link Future} of type Response */ ListenableFuture executeRequest(RequestBuilder requestBuilder); + + /*** + * Return details about pooled connections. + * + * @return a {@link ClientStats} + */ + ClientStats getClientStats(); } diff --git a/client/src/main/java/org/asynchttpclient/ClientStats.java b/client/src/main/java/org/asynchttpclient/ClientStats.java new file mode 100644 index 0000000000..625c19aa5e --- /dev/null +++ b/client/src/main/java/org/asynchttpclient/ClientStats.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 Ning, Inc. + * + * This program is licensed to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +package org.asynchttpclient; + +import java.util.Objects; + +/** + * A record class representing the state of an (@link org.asynchttpclient.AsyncHttpClient) + */ +public class ClientStats { + + private final long activeConnectionCount; + private final long idleConnectionCount; + + public ClientStats(final long activeConnectionCount, + final long idleConnectionCount) { + this.activeConnectionCount = activeConnectionCount; + this.idleConnectionCount = idleConnectionCount; + } + + /** + * @return The sum of {@link #getActiveConnectionCount()} and {@link #getIdleConnectionCount()}, + * a long representing the total number of connections in the connection pool. + */ + public long getTotalConnectionCount() { + return activeConnectionCount + idleConnectionCount; + } + + /** + * @return A long representing the number of active connection in the connection pool. + */ + public long getActiveConnectionCount() { + return activeConnectionCount; + } + + /** + * + * @return A long representing the number of idle connections in the connection pool. + */ + public long getIdleConnectionCount() { + return idleConnectionCount; + } + + @Override + public String toString() { + return "There are " + getTotalConnectionCount() + + " total connections, " + getActiveConnectionCount() + + " are active and " + getIdleConnectionCount() + " are idle."; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final ClientStats that = (ClientStats) o; + return activeConnectionCount == that.activeConnectionCount && + idleConnectionCount == that.idleConnectionCount; + } + + @Override + public int hashCode() { + return Objects.hash(activeConnectionCount, idleConnectionCount); + } +} diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java index db884e55ad..74fd6d26ab 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java +++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java @@ -255,6 +255,11 @@ public EventLoopGroup getEventLoopGroup() { return channelManager.getEventLoopGroup(); } + @Override + public ClientStats getClientStats() { + return channelManager.getClientStats(); + } + protected BoundRequestBuilder requestBuilder(String method, String url) { return new BoundRequestBuilder(this, method, config.isDisableUrlEncodingForBoundRequests()).setUrl(url).setSignatureCalculator(signatureCalculator); } diff --git a/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java index f8cea67fe6..15c43844ed 100755 --- a/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java @@ -70,4 +70,9 @@ public interface ChannelPool { * @param selector the selector */ void flushPartitions(ChannelPoolPartitionSelector selector); + + /** + * @return The number of idle channels. + */ + long getIdleChannelCount(); } diff --git a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java index f5b59fab6a..c48e48787a 100644 --- a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java @@ -50,4 +50,11 @@ public void flushPartition(Object partitionKey) { @Override public void flushPartitions(ChannelPoolPartitionSelector selector) { } + + @Override + public long getIdleChannelCount() { + return 0; + } + + } diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java index bdd559ef44..fa0fbfa03e 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java @@ -51,6 +51,7 @@ import org.asynchttpclient.AsyncHandler; import org.asynchttpclient.AsyncHttpClientConfig; +import org.asynchttpclient.ClientStats; import org.asynchttpclient.SslEngineFactory; import org.asynchttpclient.channel.ChannelPool; import org.asynchttpclient.channel.ChannelPoolPartitioning; @@ -488,4 +489,11 @@ public ChannelPool getChannelPool() { public EventLoopGroup getEventLoopGroup() { return eventLoopGroup; } + + public ClientStats getClientStats() { + final long totalConnectionCount = openChannels.size(); + final long idleConnectionCount = channelPool.getIdleChannelCount(); + final long activeConnectionCount = totalConnectionCount - idleConnectionCount; + return new ClientStats(activeConnectionCount, idleConnectionCount); + } } diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java index 878db3a290..6d21e7c079 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java @@ -356,6 +356,16 @@ public void flushPartitions(ChannelPoolPartitionSelector selector) { } } + @Override + public long getIdleChannelCount() { + return partitions.reduceValuesToLong( + Long.MAX_VALUE, + ConcurrentLinkedDeque::size, + 0, + (left, right) -> left + right + ); + } + public enum PoolLeaseStrategy { LIFO { public E lease(Deque d) { diff --git a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java new file mode 100644 index 0000000000..f7606eb627 --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java @@ -0,0 +1,169 @@ +package org.asynchttpclient; + +import static org.asynchttpclient.Dsl.asyncHttpClient; +import static org.asynchttpclient.Dsl.config; +import static org.testng.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.testng.annotations.Test; + +/** + * Created by grenville on 9/25/16. + */ +public class ClientStatsTest extends AbstractBasicTest { + + @Test(groups = "standalone") + public void testClientStatus() throws Throwable { + try (final DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config().setKeepAlive(true).setPooledConnectionIdleTimeout(5000))) { + final String url = getTargetUrl(); + + final ClientStats emptyStats = client.getClientStats(); + + assertEquals(emptyStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); + assertEquals(emptyStats.getActiveConnectionCount(), 0); + assertEquals(emptyStats.getIdleConnectionCount(), 0); + assertEquals(emptyStats.getTotalConnectionCount(), 0); + + final List> futures = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + logger.info("{} requesting url [{}]...", i, url); + futures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); + } + + Thread.sleep(2000); + + final ClientStats activeStats = client.getClientStats(); + + assertEquals(activeStats.toString(), "There are 5 total connections, 5 are active and 0 are idle."); + assertEquals(activeStats.getActiveConnectionCount(), 5); + assertEquals(activeStats.getIdleConnectionCount(), 0); + assertEquals(activeStats.getTotalConnectionCount(), 5); + + for (final ListenableFuture future : futures) { + future.get(); + } + + Thread.sleep(1000); + + final ClientStats idleStats = client.getClientStats(); + + assertEquals(idleStats.toString(), "There are 5 total connections, 0 are active and 5 are idle."); + assertEquals(idleStats.getActiveConnectionCount(), 0); + assertEquals(idleStats.getIdleConnectionCount(), 5); + assertEquals(idleStats.getTotalConnectionCount(), 5); + + // Let's make sure the active count is correct when reusing cached connections. + + final List> repeatedFutures = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + logger.info("{} requesting url [{}]...", i, url); + repeatedFutures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); + } + + Thread.sleep(2000); + + final ClientStats activeCachedStats = client.getClientStats(); + + assertEquals(activeCachedStats.toString(), "There are 5 total connections, 3 are active and 2 are idle."); + assertEquals(activeCachedStats.getActiveConnectionCount(), 3); + assertEquals(activeCachedStats.getIdleConnectionCount(), 2); + assertEquals(activeCachedStats.getTotalConnectionCount(), 5); + + for (final ListenableFuture future : repeatedFutures) { + future.get(); + } + + Thread.sleep(1000); + + final ClientStats idleCachedStats = client.getClientStats(); + + assertEquals(idleCachedStats.toString(), "There are 3 total connections, 0 are active and 3 are idle."); + assertEquals(idleCachedStats.getActiveConnectionCount(), 0); + assertEquals(idleCachedStats.getIdleConnectionCount(), 3); + assertEquals(idleCachedStats.getTotalConnectionCount(), 3); + + Thread.sleep(5000); + + final ClientStats timeoutStats = client.getClientStats(); + + assertEquals(timeoutStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); + assertEquals(timeoutStats.getActiveConnectionCount(), 0); + assertEquals(timeoutStats.getIdleConnectionCount(), 0); + assertEquals(timeoutStats.getTotalConnectionCount(), 0); + } + } + + @Test(groups = "standalone") + public void testClientStatusNoKeepalive() throws Throwable { + try (final DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config().setKeepAlive(false))) { + final String url = getTargetUrl(); + + final ClientStats emptyStats = client.getClientStats(); + + assertEquals(emptyStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); + assertEquals(emptyStats.getActiveConnectionCount(), 0); + assertEquals(emptyStats.getIdleConnectionCount(), 0); + assertEquals(emptyStats.getTotalConnectionCount(), 0); + + final List> futures = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + logger.info("{} requesting url [{}]...", i, url); + futures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); + } + + Thread.sleep(2000); + + final ClientStats activeStats = client.getClientStats(); + + assertEquals(activeStats.toString(), "There are 5 total connections, 5 are active and 0 are idle."); + assertEquals(activeStats.getActiveConnectionCount(), 5); + assertEquals(activeStats.getIdleConnectionCount(), 0); + assertEquals(activeStats.getTotalConnectionCount(), 5); + + for (final ListenableFuture future : futures) { + future.get(); + } + + Thread.sleep(1000); + + final ClientStats idleStats = client.getClientStats(); + + assertEquals(idleStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); + assertEquals(idleStats.getActiveConnectionCount(), 0); + assertEquals(idleStats.getIdleConnectionCount(), 0); + assertEquals(idleStats.getTotalConnectionCount(), 0); + + // Let's make sure the active count is correct when reusing cached connections. + + final List> repeatedFutures = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + logger.info("{} requesting url [{}]...", i, url); + repeatedFutures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); + } + + Thread.sleep(2000); + + final ClientStats activeCachedStats = client.getClientStats(); + + assertEquals(activeCachedStats.toString(), "There are 3 total connections, 3 are active and 0 are idle."); + assertEquals(activeCachedStats.getActiveConnectionCount(), 3); + assertEquals(activeCachedStats.getIdleConnectionCount(), 0); + assertEquals(activeCachedStats.getTotalConnectionCount(), 3); + + for (final ListenableFuture future : repeatedFutures) { + future.get(); + } + + Thread.sleep(1000); + + final ClientStats idleCachedStats = client.getClientStats(); + + assertEquals(idleCachedStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); + assertEquals(idleCachedStats.getActiveConnectionCount(), 0); + assertEquals(idleCachedStats.getIdleConnectionCount(), 0); + assertEquals(idleCachedStats.getTotalConnectionCount(), 0); + } + } +} diff --git a/client/src/test/java/org/asynchttpclient/test/EchoHandler.java b/client/src/test/java/org/asynchttpclient/test/EchoHandler.java index ec707ad334..71bb57561c 100644 --- a/client/src/test/java/org/asynchttpclient/test/EchoHandler.java +++ b/client/src/test/java/org/asynchttpclient/test/EchoHandler.java @@ -55,8 +55,9 @@ public void handle(String pathInContext, Request request, HttpServletRequest htt param = e.nextElement().toString(); if (param.startsWith("LockThread")) { + final int sleepTime = httpRequest.getIntHeader(param); try { - Thread.sleep(40 * 1000); + Thread.sleep(sleepTime == -1 ? 40 : sleepTime * 1000); } catch (InterruptedException ex) { } } diff --git a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java index 41c083fe51..568fb073c5 100644 --- a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java +++ b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java @@ -125,4 +125,9 @@ public ListenableFuture executeRequest(RequestBuilder requestBuilder, Asy public ListenableFuture executeRequest(RequestBuilder requestBuilder) { return null; } + + @Override + public ClientStats getClientStats() { + return null; + } } diff --git a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java index febee33bc3..d9e4fec592 100644 --- a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java +++ b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java @@ -122,4 +122,8 @@ public ListenableFuture executeRequest(RequestBuilder requestBuilder) return null; } + @Override + public ClientStats getClientStats() { + return null; + } } From 81cb9baad7f1c06608af09d64dddb91f1a50c872 Mon Sep 17 00:00:00 2001 From: Grenville Wilson Date: Mon, 31 Oct 2016 16:58:37 -0400 Subject: [PATCH 2/4] Got a little overeager with reverting my import changes --- .../org/asynchttpclient/extras/registry/BadAsyncHttpClient.java | 1 + .../org/asynchttpclient/extras/registry/TestAsyncHttpClient.java | 1 + 2 files changed, 2 insertions(+) diff --git a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java index 568fb073c5..b69893d6ad 100644 --- a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java +++ b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java @@ -15,6 +15,7 @@ import org.asynchttpclient.AsyncHandler; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.BoundRequestBuilder; +import org.asynchttpclient.ClientStats; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.ListenableFuture; import org.asynchttpclient.Request; diff --git a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java index d9e4fec592..7c5a0ca1fc 100644 --- a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java +++ b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java @@ -15,6 +15,7 @@ import org.asynchttpclient.AsyncHandler; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.BoundRequestBuilder; +import org.asynchttpclient.ClientStats; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.ListenableFuture; import org.asynchttpclient.Request; From 552e4cd2e9359228ec5f9390cc2bbf64ee957eb0 Mon Sep 17 00:00:00 2001 From: Grenville Wilson Date: Wed, 2 Nov 2016 21:08:31 -0400 Subject: [PATCH 3/4] Small tweaks and changes in response to PR comments --- .../java/org/asynchttpclient/ClientStats.java | 25 +++---- .../channel/NoopChannelPool.java | 2 - .../netty/channel/DefaultChannelPool.java | 7 +- .../org/asynchttpclient/ClientStatsTest.java | 72 ++++++++++--------- .../extras/registry/BadAsyncHttpClient.java | 2 +- .../extras/registry/TestAsyncHttpClient.java | 2 +- 6 files changed, 52 insertions(+), 58 deletions(-) diff --git a/client/src/main/java/org/asynchttpclient/ClientStats.java b/client/src/main/java/org/asynchttpclient/ClientStats.java index 625c19aa5e..ab4f931f1f 100644 --- a/client/src/main/java/org/asynchttpclient/ClientStats.java +++ b/client/src/main/java/org/asynchttpclient/ClientStats.java @@ -1,18 +1,15 @@ /* - * Copyright 2010 Ning, Inc. + * Copyright (c) 2014 AsyncHttpClient Project. All rights reserved. * - * This program is licensed to you under the Apache License, version 2.0 - * (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at + * http://www.apache.org/licenses/LICENSE-2.0. * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ package org.asynchttpclient; @@ -26,8 +23,8 @@ public class ClientStats { private final long activeConnectionCount; private final long idleConnectionCount; - public ClientStats(final long activeConnectionCount, - final long idleConnectionCount) { + public ClientStats(long activeConnectionCount, + long idleConnectionCount) { this.activeConnectionCount = activeConnectionCount; this.idleConnectionCount = idleConnectionCount; } diff --git a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java index c48e48787a..4ba0e0e8dd 100644 --- a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java @@ -55,6 +55,4 @@ public void flushPartitions(ChannelPoolPartitionSelector selector) { public long getIdleChannelCount() { return 0; } - - } diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java index 6d21e7c079..c27c1b0775 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java @@ -358,12 +358,7 @@ public void flushPartitions(ChannelPoolPartitionSelector selector) { @Override public long getIdleChannelCount() { - return partitions.reduceValuesToLong( - Long.MAX_VALUE, - ConcurrentLinkedDeque::size, - 0, - (left, right) -> left + right - ); + return partitions.values().stream().mapToLong(ConcurrentLinkedDeque::size).sum(); } public enum PoolLeaseStrategy { diff --git a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java index f7606eb627..149cf86774 100644 --- a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java +++ b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java @@ -1,3 +1,16 @@ +/* + * Copyright (c) 2014 AsyncHttpClient Project. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ package org.asynchttpclient; import static org.asynchttpclient.Dsl.asyncHttpClient; @@ -6,6 +19,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.testng.annotations.Test; @@ -16,7 +32,7 @@ public class ClientStatsTest extends AbstractBasicTest { @Test(groups = "standalone") public void testClientStatus() throws Throwable { - try (final DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config().setKeepAlive(true).setPooledConnectionIdleTimeout(5000))) { + try (final AsyncHttpClient client = asyncHttpClient(config().setKeepAlive(true).setPooledConnectionIdleTimeout(5000))) { final String url = getTargetUrl(); final ClientStats emptyStats = client.getClientStats(); @@ -26,11 +42,10 @@ public void testClientStatus() throws Throwable { assertEquals(emptyStats.getIdleConnectionCount(), 0); assertEquals(emptyStats.getTotalConnectionCount(), 0); - final List> futures = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - logger.info("{} requesting url [{}]...", i, url); - futures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); - } + final List> futures = + Stream.generate(() -> client.prepareGet(url).setHeader("LockThread","6").execute()) + .limit(5) + .collect(Collectors.toList()); Thread.sleep(2000); @@ -41,9 +56,7 @@ public void testClientStatus() throws Throwable { assertEquals(activeStats.getIdleConnectionCount(), 0); assertEquals(activeStats.getTotalConnectionCount(), 5); - for (final ListenableFuture future : futures) { - future.get(); - } + futures.forEach(future -> future.toCompletableFuture().join()); Thread.sleep(1000); @@ -56,11 +69,10 @@ public void testClientStatus() throws Throwable { // Let's make sure the active count is correct when reusing cached connections. - final List> repeatedFutures = new ArrayList<>(); - for (int i = 0; i < 3; i++) { - logger.info("{} requesting url [{}]...", i, url); - repeatedFutures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); - } + final List> repeatedFutures = + Stream.generate(() -> client.prepareGet(url).setHeader("LockThread","6").execute()) + .limit(3) + .collect(Collectors.toList()); Thread.sleep(2000); @@ -71,9 +83,7 @@ public void testClientStatus() throws Throwable { assertEquals(activeCachedStats.getIdleConnectionCount(), 2); assertEquals(activeCachedStats.getTotalConnectionCount(), 5); - for (final ListenableFuture future : repeatedFutures) { - future.get(); - } + repeatedFutures.forEach(future -> future.toCompletableFuture().join()); Thread.sleep(1000); @@ -97,7 +107,7 @@ public void testClientStatus() throws Throwable { @Test(groups = "standalone") public void testClientStatusNoKeepalive() throws Throwable { - try (final DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config().setKeepAlive(false))) { + try (final AsyncHttpClient client = asyncHttpClient(config().setKeepAlive(false))) { final String url = getTargetUrl(); final ClientStats emptyStats = client.getClientStats(); @@ -107,11 +117,10 @@ public void testClientStatusNoKeepalive() throws Throwable { assertEquals(emptyStats.getIdleConnectionCount(), 0); assertEquals(emptyStats.getTotalConnectionCount(), 0); - final List> futures = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - logger.info("{} requesting url [{}]...", i, url); - futures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); - } + final List> futures = + Stream.generate(() -> client.prepareGet(url).setHeader("LockThread","6").execute()) + .limit(5) + .collect(Collectors.toList()); Thread.sleep(2000); @@ -122,9 +131,7 @@ public void testClientStatusNoKeepalive() throws Throwable { assertEquals(activeStats.getIdleConnectionCount(), 0); assertEquals(activeStats.getTotalConnectionCount(), 5); - for (final ListenableFuture future : futures) { - future.get(); - } + futures.forEach(future -> future.toCompletableFuture().join()); Thread.sleep(1000); @@ -137,11 +144,10 @@ public void testClientStatusNoKeepalive() throws Throwable { // Let's make sure the active count is correct when reusing cached connections. - final List> repeatedFutures = new ArrayList<>(); - for (int i = 0; i < 3; i++) { - logger.info("{} requesting url [{}]...", i, url); - repeatedFutures.add(client.prepareGet(url).setHeader("LockThread", "6").execute()); - } + final List> repeatedFutures = + Stream.generate(() -> client.prepareGet(url).setHeader("LockThread","6").execute()) + .limit(3) + .collect(Collectors.toList()); Thread.sleep(2000); @@ -152,9 +158,7 @@ public void testClientStatusNoKeepalive() throws Throwable { assertEquals(activeCachedStats.getIdleConnectionCount(), 0); assertEquals(activeCachedStats.getTotalConnectionCount(), 3); - for (final ListenableFuture future : repeatedFutures) { - future.get(); - } + repeatedFutures.forEach(future -> future.toCompletableFuture().join()); Thread.sleep(1000); diff --git a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java index b69893d6ad..05ecd3f779 100644 --- a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java +++ b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/BadAsyncHttpClient.java @@ -129,6 +129,6 @@ public ListenableFuture executeRequest(RequestBuilder requestBuilder) @Override public ClientStats getClientStats() { - return null; + throw new UnsupportedOperationException(); } } diff --git a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java index 7c5a0ca1fc..fc2d5eae94 100644 --- a/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java +++ b/extras/registry/src/test/java/org/asynchttpclient/extras/registry/TestAsyncHttpClient.java @@ -125,6 +125,6 @@ public ListenableFuture executeRequest(RequestBuilder requestBuilder) @Override public ClientStats getClientStats() { - return null; + throw new UnsupportedOperationException(); } } From 5f7089ec851c5ce27ea85e143c02cfc2c0ea92e0 Mon Sep 17 00:00:00 2001 From: Grenville Wilson Date: Sun, 6 Nov 2016 00:19:10 -0400 Subject: [PATCH 4/4] Adding per-host statistics --- .../java/org/asynchttpclient/ClientStats.java | 57 +++++++++----- .../java/org/asynchttpclient/HostStats.java | 74 +++++++++++++++++++ .../asynchttpclient/channel/ChannelPool.java | 6 +- .../channel/NoopChannelPool.java | 7 +- .../netty/channel/ChannelManager.java | 37 ++++++++-- .../netty/channel/DefaultChannelPool.java | 19 ++++- .../org/asynchttpclient/ClientStatsTest.java | 60 +++++++++------ 7 files changed, 202 insertions(+), 58 deletions(-) create mode 100644 client/src/main/java/org/asynchttpclient/HostStats.java diff --git a/client/src/main/java/org/asynchttpclient/ClientStats.java b/client/src/main/java/org/asynchttpclient/ClientStats.java index ab4f931f1f..4bafd7b4c1 100644 --- a/client/src/main/java/org/asynchttpclient/ClientStats.java +++ b/client/src/main/java/org/asynchttpclient/ClientStats.java @@ -13,50 +13,68 @@ */ package org.asynchttpclient; +import java.util.Collections; +import java.util.Map; import java.util.Objects; /** - * A record class representing the state of an (@link org.asynchttpclient.AsyncHttpClient) + * A record class representing the state of an (@link org.asynchttpclient.AsyncHttpClient). */ public class ClientStats { - private final long activeConnectionCount; - private final long idleConnectionCount; + private final Map statsPerHost; - public ClientStats(long activeConnectionCount, - long idleConnectionCount) { - this.activeConnectionCount = activeConnectionCount; - this.idleConnectionCount = idleConnectionCount; + public ClientStats(Map statsPerHost) { + this.statsPerHost = Collections.unmodifiableMap(statsPerHost); } /** - * @return The sum of {@link #getActiveConnectionCount()} and {@link #getIdleConnectionCount()}, + * @return A map from hostname to statistics on that host's connections. + * The returned map is an {@link java.util.Collections.UnmodifiableMap}. + */ + public Map getStatsPerHost() { + return statsPerHost; + } + + /** + * @return The sum of {@link #getTotalActiveConnectionCount()} and {@link #getTotalIdleConnectionCount()}, * a long representing the total number of connections in the connection pool. */ public long getTotalConnectionCount() { - return activeConnectionCount + idleConnectionCount; + return statsPerHost + .values() + .stream() + .mapToLong(HostStats::getHostConnectionCount) + .sum(); } /** - * @return A long representing the number of active connection in the connection pool. + * @return A long representing the number of active connections in the connection pool. */ - public long getActiveConnectionCount() { - return activeConnectionCount; + public long getTotalActiveConnectionCount() { + return statsPerHost + .values() + .stream() + .mapToLong(HostStats::getHostActiveConnectionCount) + .sum(); } /** - * * @return A long representing the number of idle connections in the connection pool. */ - public long getIdleConnectionCount() { - return idleConnectionCount; + public long getTotalIdleConnectionCount() { + return statsPerHost + .values() + .stream() + .mapToLong(HostStats::getHostIdleConnectionCount) + .sum(); } @Override public String toString() { return "There are " + getTotalConnectionCount() + - " total connections, " + getActiveConnectionCount() + - " are active and " + getIdleConnectionCount() + " are idle."; + " total connections, " + getTotalActiveConnectionCount() + + " are active and " + getTotalIdleConnectionCount() + " are idle."; } @Override @@ -64,12 +82,11 @@ public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final ClientStats that = (ClientStats) o; - return activeConnectionCount == that.activeConnectionCount && - idleConnectionCount == that.idleConnectionCount; + return Objects.equals(statsPerHost, that.statsPerHost); } @Override public int hashCode() { - return Objects.hash(activeConnectionCount, idleConnectionCount); + return Objects.hashCode(statsPerHost); } } diff --git a/client/src/main/java/org/asynchttpclient/HostStats.java b/client/src/main/java/org/asynchttpclient/HostStats.java new file mode 100644 index 0000000000..87d9278820 --- /dev/null +++ b/client/src/main/java/org/asynchttpclient/HostStats.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 AsyncHttpClient Project. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package org.asynchttpclient; + +import java.util.Objects; + +/** + * A record class representing the status of connections to some host. + */ +public class HostStats { + + private final long activeConnectionCount; + private final long idleConnectionCount; + + public HostStats(long activeConnectionCount, + long idleConnectionCount) { + this.activeConnectionCount = activeConnectionCount; + this.idleConnectionCount = idleConnectionCount; + } + + /** + * @return The sum of {@link #getHostActiveConnectionCount()} and {@link #getHostIdleConnectionCount()}, + * a long representing the total number of connections to this host. + */ + public long getHostConnectionCount() { + return activeConnectionCount + idleConnectionCount; + } + + /** + * @return A long representing the number of active connections to the host. + */ + public long getHostActiveConnectionCount() { + return activeConnectionCount; + } + + /** + * @return A long representing the number of idle connections in the connection pool. + */ + public long getHostIdleConnectionCount() { + return idleConnectionCount; + } + + @Override + public String toString() { + return "There are " + getHostConnectionCount() + + " total connections, " + getHostActiveConnectionCount() + + " are active and " + getHostIdleConnectionCount() + " are idle."; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final HostStats hostStats = (HostStats) o; + return activeConnectionCount == hostStats.activeConnectionCount && + idleConnectionCount == hostStats.idleConnectionCount; + } + + @Override + public int hashCode() { + return Objects.hash(activeConnectionCount, idleConnectionCount); + } +} diff --git a/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java index 15c43844ed..0d20df349d 100755 --- a/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java @@ -13,6 +13,8 @@ */ package org.asynchttpclient.channel; +import java.util.Map; + import io.netty.channel.Channel; public interface ChannelPool { @@ -72,7 +74,7 @@ public interface ChannelPool { void flushPartitions(ChannelPoolPartitionSelector selector); /** - * @return The number of idle channels. + * @return The number of idle channels per host. */ - long getIdleChannelCount(); + Map getIdleChannelCountPerHost(); } diff --git a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java index 4ba0e0e8dd..30ec3875e2 100644 --- a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java @@ -13,6 +13,9 @@ */ package org.asynchttpclient.channel; +import java.util.Collections; +import java.util.Map; + import io.netty.channel.Channel; public enum NoopChannelPool implements ChannelPool { @@ -52,7 +55,7 @@ public void flushPartitions(ChannelPoolPartitionSelector selector) { } @Override - public long getIdleChannelCount() { - return 0; + public Map getIdleChannelCountPerHost() { + return Collections.emptyMap(); } } diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java index fa0fbfa03e..8b45754559 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java @@ -40,19 +40,23 @@ import io.netty.util.concurrent.GlobalEventExecutor; import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.OptionalLong; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; -import org.asynchttpclient.AsyncHandler; -import org.asynchttpclient.AsyncHttpClientConfig; -import org.asynchttpclient.ClientStats; -import org.asynchttpclient.SslEngineFactory; +import org.asynchttpclient.*; import org.asynchttpclient.channel.ChannelPool; import org.asynchttpclient.channel.ChannelPoolPartitioning; import org.asynchttpclient.channel.NoopChannelPool; @@ -491,9 +495,26 @@ public EventLoopGroup getEventLoopGroup() { } public ClientStats getClientStats() { - final long totalConnectionCount = openChannels.size(); - final long idleConnectionCount = channelPool.getIdleChannelCount(); - final long activeConnectionCount = totalConnectionCount - idleConnectionCount; - return new ClientStats(activeConnectionCount, idleConnectionCount); + final Map totalConnectionsPerHost = openChannels + .stream() + .map(Channel::remoteAddress) + .filter(a -> a.getClass() == InetSocketAddress.class) + .map(a -> (InetSocketAddress) a) + .map(InetSocketAddress::getHostName) + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + final Map idleConnectionsPerHost = channelPool.getIdleChannelCountPerHost(); + final Map statsPerHost = totalConnectionsPerHost + .entrySet() + .stream() + .collect(Collectors.toMap( + Entry::getKey, + entry -> { + final long totalConnectionCount = entry.getValue(); + final long idleConnectionCount = idleConnectionsPerHost.getOrDefault(entry.getKey(), 0L); + final long activeConnectionCount = totalConnectionCount - idleConnectionCount; + return new HostStats(activeConnectionCount, idleConnectionCount); + } + )); + return new ClientStats(statsPerHost); } } diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java index c27c1b0775..0a8312bae1 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java @@ -21,11 +21,14 @@ import io.netty.util.Timer; import io.netty.util.TimerTask; +import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.stream.Collectors; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.channel.ChannelPool; @@ -120,6 +123,10 @@ public boolean takeOwnership() { return owned.compareAndSet(false, true); } + public Channel getChannel() { + return channel; + } + @Override // only depends on channel public boolean equals(Object o) { @@ -357,8 +364,16 @@ public void flushPartitions(ChannelPoolPartitionSelector selector) { } @Override - public long getIdleChannelCount() { - return partitions.values().stream().mapToLong(ConcurrentLinkedDeque::size).sum(); + public Map getIdleChannelCountPerHost() { + return partitions + .values() + .stream() + .flatMap(ConcurrentLinkedDeque::stream) + .map(idle -> idle.getChannel().remoteAddress()) + .filter(a -> a.getClass() == InetSocketAddress.class) + .map(a -> (InetSocketAddress) a) + .map(InetSocketAddress::getHostName) + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); } public enum PoolLeaseStrategy { diff --git a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java index 149cf86774..10c04d10d1 100644 --- a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java +++ b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java @@ -16,10 +16,9 @@ import static org.asynchttpclient.Dsl.asyncHttpClient; import static org.asynchttpclient.Dsl.config; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -30,6 +29,8 @@ */ public class ClientStatsTest extends AbstractBasicTest { + private final static String hostname = "localhost"; + @Test(groups = "standalone") public void testClientStatus() throws Throwable { try (final AsyncHttpClient client = asyncHttpClient(config().setKeepAlive(true).setPooledConnectionIdleTimeout(5000))) { @@ -38,9 +39,10 @@ public void testClientStatus() throws Throwable { final ClientStats emptyStats = client.getClientStats(); assertEquals(emptyStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); - assertEquals(emptyStats.getActiveConnectionCount(), 0); - assertEquals(emptyStats.getIdleConnectionCount(), 0); + assertEquals(emptyStats.getTotalActiveConnectionCount(), 0); + assertEquals(emptyStats.getTotalIdleConnectionCount(), 0); assertEquals(emptyStats.getTotalConnectionCount(), 0); + assertNull(emptyStats.getStatsPerHost().get(hostname)); final List> futures = Stream.generate(() -> client.prepareGet(url).setHeader("LockThread","6").execute()) @@ -52,9 +54,10 @@ public void testClientStatus() throws Throwable { final ClientStats activeStats = client.getClientStats(); assertEquals(activeStats.toString(), "There are 5 total connections, 5 are active and 0 are idle."); - assertEquals(activeStats.getActiveConnectionCount(), 5); - assertEquals(activeStats.getIdleConnectionCount(), 0); + assertEquals(activeStats.getTotalActiveConnectionCount(), 5); + assertEquals(activeStats.getTotalIdleConnectionCount(), 0); assertEquals(activeStats.getTotalConnectionCount(), 5); + assertEquals(activeStats.getStatsPerHost().get(hostname).getHostConnectionCount(), 5); futures.forEach(future -> future.toCompletableFuture().join()); @@ -63,9 +66,10 @@ public void testClientStatus() throws Throwable { final ClientStats idleStats = client.getClientStats(); assertEquals(idleStats.toString(), "There are 5 total connections, 0 are active and 5 are idle."); - assertEquals(idleStats.getActiveConnectionCount(), 0); - assertEquals(idleStats.getIdleConnectionCount(), 5); + assertEquals(idleStats.getTotalActiveConnectionCount(), 0); + assertEquals(idleStats.getTotalIdleConnectionCount(), 5); assertEquals(idleStats.getTotalConnectionCount(), 5); + assertEquals(idleStats.getStatsPerHost().get(hostname).getHostConnectionCount(), 5); // Let's make sure the active count is correct when reusing cached connections. @@ -79,9 +83,10 @@ public void testClientStatus() throws Throwable { final ClientStats activeCachedStats = client.getClientStats(); assertEquals(activeCachedStats.toString(), "There are 5 total connections, 3 are active and 2 are idle."); - assertEquals(activeCachedStats.getActiveConnectionCount(), 3); - assertEquals(activeCachedStats.getIdleConnectionCount(), 2); + assertEquals(activeCachedStats.getTotalActiveConnectionCount(), 3); + assertEquals(activeCachedStats.getTotalIdleConnectionCount(), 2); assertEquals(activeCachedStats.getTotalConnectionCount(), 5); + assertEquals(activeCachedStats.getStatsPerHost().get(hostname).getHostConnectionCount(), 5); repeatedFutures.forEach(future -> future.toCompletableFuture().join()); @@ -90,18 +95,20 @@ public void testClientStatus() throws Throwable { final ClientStats idleCachedStats = client.getClientStats(); assertEquals(idleCachedStats.toString(), "There are 3 total connections, 0 are active and 3 are idle."); - assertEquals(idleCachedStats.getActiveConnectionCount(), 0); - assertEquals(idleCachedStats.getIdleConnectionCount(), 3); + assertEquals(idleCachedStats.getTotalActiveConnectionCount(), 0); + assertEquals(idleCachedStats.getTotalIdleConnectionCount(), 3); assertEquals(idleCachedStats.getTotalConnectionCount(), 3); + assertEquals(idleCachedStats.getStatsPerHost().get(hostname).getHostConnectionCount(), 3); Thread.sleep(5000); final ClientStats timeoutStats = client.getClientStats(); assertEquals(timeoutStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); - assertEquals(timeoutStats.getActiveConnectionCount(), 0); - assertEquals(timeoutStats.getIdleConnectionCount(), 0); + assertEquals(timeoutStats.getTotalActiveConnectionCount(), 0); + assertEquals(timeoutStats.getTotalIdleConnectionCount(), 0); assertEquals(timeoutStats.getTotalConnectionCount(), 0); + assertNull(timeoutStats.getStatsPerHost().get(hostname)); } } @@ -113,9 +120,10 @@ public void testClientStatusNoKeepalive() throws Throwable { final ClientStats emptyStats = client.getClientStats(); assertEquals(emptyStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); - assertEquals(emptyStats.getActiveConnectionCount(), 0); - assertEquals(emptyStats.getIdleConnectionCount(), 0); + assertEquals(emptyStats.getTotalActiveConnectionCount(), 0); + assertEquals(emptyStats.getTotalIdleConnectionCount(), 0); assertEquals(emptyStats.getTotalConnectionCount(), 0); + assertNull(emptyStats.getStatsPerHost().get(hostname)); final List> futures = Stream.generate(() -> client.prepareGet(url).setHeader("LockThread","6").execute()) @@ -127,9 +135,10 @@ public void testClientStatusNoKeepalive() throws Throwable { final ClientStats activeStats = client.getClientStats(); assertEquals(activeStats.toString(), "There are 5 total connections, 5 are active and 0 are idle."); - assertEquals(activeStats.getActiveConnectionCount(), 5); - assertEquals(activeStats.getIdleConnectionCount(), 0); + assertEquals(activeStats.getTotalActiveConnectionCount(), 5); + assertEquals(activeStats.getTotalIdleConnectionCount(), 0); assertEquals(activeStats.getTotalConnectionCount(), 5); + assertEquals(activeStats.getStatsPerHost().get(hostname).getHostConnectionCount(), 5); futures.forEach(future -> future.toCompletableFuture().join()); @@ -138,9 +147,10 @@ public void testClientStatusNoKeepalive() throws Throwable { final ClientStats idleStats = client.getClientStats(); assertEquals(idleStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); - assertEquals(idleStats.getActiveConnectionCount(), 0); - assertEquals(idleStats.getIdleConnectionCount(), 0); + assertEquals(idleStats.getTotalActiveConnectionCount(), 0); + assertEquals(idleStats.getTotalIdleConnectionCount(), 0); assertEquals(idleStats.getTotalConnectionCount(), 0); + assertNull(idleStats.getStatsPerHost().get(hostname)); // Let's make sure the active count is correct when reusing cached connections. @@ -154,9 +164,10 @@ public void testClientStatusNoKeepalive() throws Throwable { final ClientStats activeCachedStats = client.getClientStats(); assertEquals(activeCachedStats.toString(), "There are 3 total connections, 3 are active and 0 are idle."); - assertEquals(activeCachedStats.getActiveConnectionCount(), 3); - assertEquals(activeCachedStats.getIdleConnectionCount(), 0); + assertEquals(activeCachedStats.getTotalActiveConnectionCount(), 3); + assertEquals(activeCachedStats.getTotalIdleConnectionCount(), 0); assertEquals(activeCachedStats.getTotalConnectionCount(), 3); + assertEquals(activeCachedStats.getStatsPerHost().get(hostname).getHostConnectionCount(), 3); repeatedFutures.forEach(future -> future.toCompletableFuture().join()); @@ -165,9 +176,10 @@ public void testClientStatusNoKeepalive() throws Throwable { final ClientStats idleCachedStats = client.getClientStats(); assertEquals(idleCachedStats.toString(), "There are 0 total connections, 0 are active and 0 are idle."); - assertEquals(idleCachedStats.getActiveConnectionCount(), 0); - assertEquals(idleCachedStats.getIdleConnectionCount(), 0); + assertEquals(idleCachedStats.getTotalActiveConnectionCount(), 0); + assertEquals(idleCachedStats.getTotalIdleConnectionCount(), 0); assertEquals(idleCachedStats.getTotalConnectionCount(), 0); + assertNull(idleCachedStats.getStatsPerHost().get(hostname)); } } }