Improvements to the Jetty client documentation, connection pool section.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-04-04 11:50:48 +02:00
parent 51c42f2849
commit 6e0b5f387b
4 changed files with 133 additions and 20 deletions

View File

@ -37,10 +37,10 @@ participant Application
participant HttpClient
participant Server
Application -> Server: GET /path
Server -> HttpClient: 401 + WWW-Authenticate
HttpClient -> Server: GET + Authentication
Server -> Application: 200 OK
Application -> Server : GET /path
Server -> HttpClient : 401 + WWW-Authenticate
HttpClient -> Server : GET + Authentication
Server -> Application : 200 OK
----
Upon receiving a HTTP 401 response code, `HttpClient` looks at the

View File

@ -108,8 +108,8 @@ following components:
* a set of _destinations_.
A _destination_ is the client-side component that represent an _origin_ on
a server, and manages a queue of requests for that origin, and a pool of
connections to that origin.
a server, and manages a queue of requests for that origin, and a
link:#client-http-connection-pool[pool of connections] to that origin.
An _origin_ may be simply thought as the tuple `(scheme, host, port)` and it
is where the client connects to in order to communicate with the server.
@ -145,9 +145,75 @@ connection pools.
Therefore an origin is identified by the tuple
`(scheme, host, port, tag, protocol)`.
[[client-http-connection-pool]]
==== HttpClient Connection Pooling
A destination manages a `org.eclipse.jetty.client.ConnectionPool`, where
connections to a particular origin are pooled for performance reasons:
opening a connection is a costly operation and it's better to reuse them
for multiple requests.
NOTE: Remember that to select a specific destination you must select a
specific origin, and that an origin is identified by the tuple
`(scheme, host, port, tag, protocol)`, so you can have multiple destinations
for the same `host` and `port`.
You can access the `ConnectionPool` in this way:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=getConnectionPool]
----
Jetty's client library provides the following `ConnectionPool` implementations:
* `DuplexConnectionPool`, historically the first implementation, only used by
the HTTP/1.1 transport.
* `MultiplexConnectionPool`, the generic implementation valid for any transport
where connections are reused with a MRU (most recently used) algorithm (that is,
the connections most recently returned to the connection pool are the more
likely to be used again).
* `RoundRobinConnectionPool`, similar to `MultiplexConnectionPool` but where
connections are reused with a round-robin algorithm.
The `ConnectionPool` implementation can be customized for each destination in
by setting a `ConnectionPool.Factory` on the `HttpClientTransport`:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=setConnectionPool]
----
[[client-http-request-processing]]
==== HttpClient Request Processing
[plantuml]
----
skinparam backgroundColor transparent
skinparam monochrome true
skinparam shadowing false
participant Application
participant Request
participant HttpClient
participant Destination
participant ConnectionPool
participant Connection
Application -> HttpClient : newRequest()
HttpClient -> Request **
Application -> Request : send()
Request -> HttpClient : send()
HttpClient -> Destination ** : get or create
Destination -> ConnectionPool ** : create
HttpClient -> Destination : send(Request)
Destination -> Destination : enqueue(Request)
Destination -> ConnectionPool : acquire()
ConnectionPool -> Connection ** : create
Destination -> Destination : dequeue(Request)
Destination -> Connection : send(Request)
----
When a request is sent, an origin is computed from the request; `HttpClient`
uses that origin to find (or create if it does not exist) the correspondent
destination.
@ -160,8 +226,8 @@ and sends it over the connection.
The first request to a destination triggers the opening of the first
connection.
A second request with the same origin sent _after_ the first will reuse the
same connection.
A second request with the same origin sent _after_ the first request/response
cycle is completed will reuse the same connection.
A second request with the same origin sent _concurrently_ with the first
request will cause the opening of a second connection.
The configuration parameter `HttpClient.maxConnectionsPerDestination`
@ -171,7 +237,7 @@ the max number of connections that can be opened for a destination.
NOTE: If opening connections to a given origin takes a long time, then
requests for that origin will queue up in the corresponding destination.
Each connection can handle a limited number of requests.
Each connection can handle a limited number of concurrent requests.
For HTTP/1.1, this number is always `1`: there can only be one outstanding
request for each connection.
For HTTP/2 this number is determined by the server `max_concurrent_stream`

View File

@ -71,17 +71,17 @@ participant HttpClient
participant Proxy
participant Server
Application -> Proxy: GET /path
Proxy -> HttpClient: 407 + Proxy-Authenticate
HttpClient -> Proxy: GET /path + Proxy-Authorization
Proxy -> Server: GET /path
Server -> Proxy: 401 + WWW-Authenticate
Proxy -> HttpClient: 401 + WWW-Authenticate
HttpClient -> Proxy: GET /path + Proxy-Authorization + Authorization
Proxy -> Server: GET /path + Authorization
Server -> Proxy: 200 OK
Proxy -> HttpClient: 200 OK
HttpClient -> Application: 200 OK
Application -> Proxy : GET /path
Proxy -> HttpClient : 407 + Proxy-Authenticate
HttpClient -> Proxy : GET /path + Proxy-Authorization
Proxy -> Server : GET /path
Server -> Proxy : 401 + WWW-Authenticate
Proxy -> HttpClient : 401 + WWW-Authenticate
HttpClient -> Proxy : GET /path + Proxy-Authorization + Authorization
Proxy -> Server : GET /path + Authorization
Server -> Proxy : 200 OK
Proxy -> HttpClient : 200 OK
HttpClient -> Application : 200 OK
----
The application does not receive events related to the responses with code 407

View File

@ -31,9 +31,13 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.ProxyConfiguration;
import org.eclipse.jetty.client.RoundRobinConnectionPool;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse;
@ -800,4 +804,47 @@ public class HTTPClientDocs
.send();
// end::dynamicClearText[]
}
public void getConnectionPool() throws Exception
{
// tag::getConnectionPool[]
HttpClient httpClient = new HttpClient();
httpClient.start();
ConnectionPool connectionPool = httpClient.getDestinations().stream()
// Cast to HttpDestination.
.map(HttpDestination.class::cast)
// Find the destination by filtering on the Origin.
.filter(destination -> destination.getOrigin().getAddress().getHost().equals("domain.com"))
.findAny()
// Get the ConnectionPool.
.map(HttpDestination::getConnectionPool)
.orElse(null);
// end::getConnectionPool[]
}
public void setConnectionPool() throws Exception
{
// tag::setConnectionPool[]
HttpClient httpClient = new HttpClient();
httpClient.start();
// The max number of connections in the pool.
int maxConnectionsPerDestination = httpClient.getMaxConnectionsPerDestination();
// The max number of requests per connection (multiplexing).
// Start with 1, since this value is dynamically set to larger values if
// the transport supports multiplexing requests on the same connection.
int maxRequestsPerConnection = 1;
HttpClientTransport transport = httpClient.getTransport();
// Set the ConnectionPool.Factory using a lambda.
transport.setConnectionPoolFactory(destination ->
new RoundRobinConnectionPool(destination,
maxConnectionsPerDestination,
destination,
maxRequestsPerConnection));
// end::setConnectionPool[]
}
}