Skip to content

Commit

Permalink
New HTTP client connection pooling (#8100)
Browse files Browse the repository at this point in the history
This PR adds a new connection pooling infrastructure to the HTTP client. The biggest changes are in ConnectionManager, which contains network setup code, and in the new PoolResizer class, which handles pool dynamics (it calls into ConnectionManager.Pool to assign requests, establish new connections, etc, but does no network setup by itself).

Changes in no particular order:

- The `http-version` config setting has been replaced by two settings. `plaintext-mode` applies to `http` URLs and can be set to either `HTTP_1` or `H2C`. `alpn-modes` applies to `https` URLs, and determines the protocols that should be supported in protocol negotiation (it's a list, with the supported values `h2` and `http/1.1`). The old http-version setting remains for compatibility, and should remain for 4.0. It is also used in some test cases.
- connection pooling now applies to all connections (except websockets), not just exchange calls, and it is enabled by default.
- HTTP2 connections now use a channel-per-stream model (like #6842), and allow concurrent requests on one connection.
- Connection pool configuration is more fine-grained, with separate settings for HTTP1 and HTTP2.
- Pooled connections are now tracked using netty's resource leak detection, to avoid "dangling" connections in the pool (note: resource leak detection is still only tested widely for the tests in the http-server-netty module atm).
- One minor fix in the http server that came up in the test suite because connection pooling is on by default now.

There are still some TODOs in this PR. Some relate to pending netty improvements (netty/netty#12827 , netty/netty#12830), but work fine for the moment. Some are future improvements that could be made, but that I want to do in separate PRs.
  • Loading branch information
yawkat committed Oct 19, 2022
1 parent 50ae5bc commit 0f34209
Show file tree
Hide file tree
Showing 43 changed files with 3,694 additions and 1,434 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.ThreadFactory;
Expand Down Expand Up @@ -145,7 +148,16 @@ public abstract class HttpClientConfiguration {

private String eventLoopGroup = "default";

private HttpVersion httpVersion = HttpVersion.HTTP_1_1;
@Deprecated
@Nullable
private HttpVersion httpVersion = null;

private HttpVersionSelection.PlaintextMode plaintextMode = HttpVersionSelection.PlaintextMode.HTTP_1;

private List<String> alpnModes = Arrays.asList(
HttpVersionSelection.ALPN_HTTP_2,
HttpVersionSelection.ALPN_HTTP_1
);

private LogLevel logLevel;

Expand Down Expand Up @@ -201,15 +213,23 @@ public HttpClientConfiguration(HttpClientConfiguration copy) {
/**
* The HTTP version to use. Defaults to {@link HttpVersion#HTTP_1_1}.
* @return The http version
* @deprecated There are now separate settings for HTTP and HTTPS connections. To configure
* HTTP connections (e.g. for h2c), use {@link #plaintextMode}. To configure ALPN, set
* {@link #alpnModes}.
*/
@Deprecated
public HttpVersion getHttpVersion() {
return httpVersion;
}

/**
* Sets the HTTP version to use. Defaults to {@link HttpVersion#HTTP_1_1}.
* @param httpVersion The http version
* @deprecated There are now separate settings for HTTP and HTTPS connections. To configure
* HTTP connections (e.g. for h2c), use {@link #plaintextMode}. To configure ALPN, set
* {@link #alpnModes}.
*/
@Deprecated
public void setHttpVersion(HttpVersion httpVersion) {
if (httpVersion != null) {
this.httpVersion = httpVersion;
Expand Down Expand Up @@ -637,6 +657,58 @@ public Proxy resolveProxy(boolean isSsl, String host, int port) {
}
}

/**
* The connection mode to use for <i>plaintext</i> (http as opposed to https) connections.
* <br>
* <b>Note: If {@link #httpVersion} is set, this setting is ignored!</b>
*
* @return The plaintext connection mode.
* @since 4.0.0
*/
@NonNull
public HttpVersionSelection.PlaintextMode getPlaintextMode() {
return plaintextMode;
}

/**
* The connection mode to use for <i>plaintext</i> (http as opposed to https) connections.
* <br>
* <b>Note: If {@link #httpVersion} is set, this setting is ignored!</b>
*
* @param plaintextMode The plaintext connection mode.
* @since 4.0.0
*/
public void setPlaintextMode(@NonNull HttpVersionSelection.PlaintextMode plaintextMode) {
this.plaintextMode = Objects.requireNonNull(plaintextMode, "plaintextMode");
}

/**
* The protocols to support for TLS ALPN. If HTTP 2 is included, this will also restrict the
* TLS cipher suites to those supported by the HTTP 2 standard.
* <br>
* <b>Note: If {@link #httpVersion} is set, this setting is ignored!</b>
*
* @return The supported ALPN protocols.
* @since 4.0.0
*/
@NonNull
public List<String> getAlpnModes() {
return alpnModes;
}

/**
* The protocols to support for TLS ALPN. If HTTP 2 is included, this will also restrict the
* TLS cipher suites to those supported by the HTTP 2 standard.
* <br>
* <b>Note: If {@link #httpVersion} is set, this setting is ignored!</b>
*
* @param alpnModes The supported ALPN protocols.
* @since 4.0.0
*/
public void setAlpnModes(@NonNull List<String> alpnModes) {
this.alpnModes = Objects.requireNonNull(alpnModes, "alpnModes");
}

/**
* Configuration for the HTTP client connnection pool.
*/
Expand All @@ -650,15 +722,13 @@ public static class ConnectionPoolConfiguration implements Toggleable {
* The default enable value.
*/
@SuppressWarnings("WeakerAccess")
public static final boolean DEFAULT_ENABLED = false;
public static final boolean DEFAULT_ENABLED = true;

/**
* The default max connections value.
*/
@SuppressWarnings("WeakerAccess")
public static final int DEFAULT_MAXCONNECTIONS = -1;
private int maxPendingConnections = 4;

private int maxConnections = DEFAULT_MAXCONNECTIONS;
private int maxConcurrentRequestsPerHttp2Connection = Integer.MAX_VALUE;
private int maxConcurrentHttp1Connections = Integer.MAX_VALUE;
private int maxConcurrentHttp2Connections = 1;

private int maxPendingAcquires = Integer.MAX_VALUE;

Expand All @@ -685,24 +755,6 @@ public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

/**
* The maximum number of connections. Defaults to ({@value io.micronaut.http.client.HttpClientConfiguration.ConnectionPoolConfiguration#DEFAULT_MAXCONNECTIONS}); no maximum.
*
* @return The max connections
*/
public int getMaxConnections() {
return maxConnections;
}

/**
* Sets the maximum number of connections. Defaults to no maximum.
*
* @param maxConnections The count
*/
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}

/**
* Maximum number of futures awaiting connection acquisition. Defaults to no maximum.
*
Expand Down Expand Up @@ -738,5 +790,90 @@ public Optional<Duration> getAcquireTimeout() {
public void setAcquireTimeout(@Nullable Duration acquireTimeout) {
this.acquireTimeout = acquireTimeout;
}

/**
* The maximum number of <i>pending</i> (new) connections before they are assigned to a
* pool.
*
* @return The maximum number of pending connections
* @since 4.0.0
*/
public int getMaxPendingConnections() {
return maxPendingConnections;
}

/**
* The maximum number of <i>pending</i> (new) connections before they are assigned to a
* pool.
*
* @param maxPendingConnections The maximum number of pending connections
* @since 4.0.0
*/
public void setMaxPendingConnections(int maxPendingConnections) {
this.maxPendingConnections = maxPendingConnections;
}

/**
* The maximum number of requests (streams) that can run concurrently on one HTTP2
* connection.
*
* @return The maximum concurrent request count
* @since 4.0.0
*/
public int getMaxConcurrentRequestsPerHttp2Connection() {
return maxConcurrentRequestsPerHttp2Connection;
}

/**
* The maximum number of requests (streams) that can run concurrently on one HTTP2
* connection.
*
* @param maxConcurrentRequestsPerHttp2Connection The maximum concurrent request count
* @since 4.0.0
*/
public void setMaxConcurrentRequestsPerHttp2Connection(int maxConcurrentRequestsPerHttp2Connection) {
this.maxConcurrentRequestsPerHttp2Connection = maxConcurrentRequestsPerHttp2Connection;
}

/**
* The maximum number of concurrent HTTP1 connections in the pool.
*
* @return The maximum concurrent connection count
* @since 4.0.0
*/
public int getMaxConcurrentHttp1Connections() {
return maxConcurrentHttp1Connections;
}

/**
* The maximum number of concurrent HTTP1 connections in the pool.
*
* @param maxConcurrentHttp1Connections The maximum concurrent connection count
* @since 4.0.0
*/
public void setMaxConcurrentHttp1Connections(int maxConcurrentHttp1Connections) {
this.maxConcurrentHttp1Connections = maxConcurrentHttp1Connections;
}

/**
* The maximum number of concurrent HTTP2 connections in the pool.
*
* @return The maximum concurrent connection count
* @since 4.0.0
*/
public int getMaxConcurrentHttp2Connections() {
return maxConcurrentHttp2Connections;
}

/**
* The maximum number of concurrent HTTP2 connections in the pool.
*
* @param maxConcurrentHttp2Connections The maximum concurrent connection count
* @since 4.0.0
*/
public void setMaxConcurrentHttp2Connections(int maxConcurrentHttp2Connections) {
this.maxConcurrentHttp2Connections = maxConcurrentHttp2Connections;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,25 @@ public interface HttpClientRegistry<T extends HttpClient> {
* @param clientId The client ID
* @param path The path (Optional)
* @return The client
* @deprecated Use {@link #getClient(HttpVersionSelection, String, String)} instead
*/
@Deprecated
@NonNull
T getClient(HttpVersion httpVersion, @NonNull String clientId, @Nullable String path);
default T getClient(HttpVersion httpVersion, @NonNull String clientId, @Nullable String path) {
return getClient(HttpVersionSelection.forLegacyVersion(httpVersion), clientId, path);
}

/**
* Return the client for the client ID and path.
*
* @param httpVersion The HTTP version
* @param clientId The client ID
* @param path The path (Optional)
* @return The client
* @since 4.0.0
*/
@NonNull
T getClient(@NonNull HttpVersionSelection httpVersion, @NonNull String clientId, @Nullable String path);

/**
* Resolves a {@link HttpClient} for the given injection point.
Expand Down

0 comments on commit 0f34209

Please sign in to comment.