-
Notifications
You must be signed in to change notification settings - Fork 1.6k
#1130: Diagnostics/debug info for AsyncHttpClient #1255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| /* | ||
| * 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.Collections; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * A record class representing the state of an (@link org.asynchttpclient.AsyncHttpClient). | ||
| */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. javadoc |
||
| public class ClientStats { | ||
|
|
||
| private final Map<String, HostStats> statsPerHost; | ||
|
|
||
| public ClientStats(Map<String, HostStats> statsPerHost) { | ||
| this.statsPerHost = Collections.unmodifiableMap(statsPerHost); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Java doesn't have immutable collections and Guava (with its |
||
| } | ||
|
|
||
| /** | ||
| * @return A map from hostname to statistics on that host's connections. | ||
| * The returned map is an {@link java.util.Collections.UnmodifiableMap}. | ||
| */ | ||
| public Map<String, HostStats> 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() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. javadoc |
||
| return statsPerHost | ||
| .values() | ||
| .stream() | ||
| .mapToLong(HostStats::getHostConnectionCount) | ||
| .sum(); | ||
| } | ||
|
|
||
| /** | ||
| * @return A long representing the number of active connections in the connection pool. | ||
| */ | ||
| 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 getTotalIdleConnectionCount() { | ||
| return statsPerHost | ||
| .values() | ||
| .stream() | ||
| .mapToLong(HostStats::getHostIdleConnectionCount) | ||
| .sum(); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Went ahead and kept the old |
||
| return "There are " + getTotalConnectionCount() + | ||
| " total connections, " + getTotalActiveConnectionCount() + | ||
| " are active and " + getTotalIdleConnectionCount() + " 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 Objects.equals(statsPerHost, that.statsPerHost); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hashCode(statsPerHost); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. implement |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -255,6 +255,11 @@ public EventLoopGroup getEventLoopGroup() { | |
| return channelManager.getEventLoopGroup(); | ||
| } | ||
|
|
||
| @Override | ||
| public ClientStats getClientStats() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be declared in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Performing the computation should be the responsibility of |
||
| return channelManager.getClientStats(); | ||
| } | ||
|
|
||
| protected BoundRequestBuilder requestBuilder(String method, String url) { | ||
| return new BoundRequestBuilder(this, method, config.isDisableUrlEncodingForBoundRequests()).setUrl(url).setSignatureCalculator(signatureCalculator); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,18 +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.SslEngineFactory; | ||
| import org.asynchttpclient.*; | ||
| import org.asynchttpclient.channel.ChannelPool; | ||
| import org.asynchttpclient.channel.ChannelPoolPartitioning; | ||
| import org.asynchttpclient.channel.NoopChannelPool; | ||
|
|
@@ -488,4 +493,28 @@ public ChannelPool getChannelPool() { | |
| public EventLoopGroup getEventLoopGroup() { | ||
| return eventLoopGroup; | ||
| } | ||
|
|
||
| public ClientStats getClientStats() { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The implementation of this method does not make me happy, but I really don't see a better way to do this with Java 8 |
||
| final Map<String, Long> totalConnectionsPerHost = openChannels | ||
| .stream() | ||
| .map(Channel::remoteAddress) | ||
| .filter(a -> a.getClass() == InetSocketAddress.class) | ||
| .map(a -> (InetSocketAddress) a) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this sucks, but I didn't really see another way out. Are there any other types of socket that are likely to live here or in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's pretty safe to cast
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the filter beforehand means we're not going to see an exception. I was just worried about leaving out other kinds of sockets from the stats, but it sounds like we're good. |
||
| .map(InetSocketAddress::getHostName) | ||
| .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); | ||
| final Map<String, Long> idleConnectionsPerHost = channelPool.getIdleChannelCountPerHost(); | ||
| final Map<String, HostStats> 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); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's aim for AHC 2.1 so we can change this API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I should reopen this PR against branch "2.1", right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. The 2.1 branch will be dropped by a few weeks when I'll move the master to Netty 4.1. This branch was solely for the Play people so they can make progress on their own upgrade. It lags behind master in terms for bug fixes and refactoring.