Make ReactorClientHttpConnector lifecycle-aware
With this commit, ReactorClientHttpConnector now implements SmartLifecycle which optionally allows recreating the HttpClient after ReactorResourceFactory has been updated. Closes gh-31180
This commit is contained in:
@@ -20,6 +20,8 @@ import java.net.URI;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.netty.NettyOutbound;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
@@ -27,23 +29,42 @@ import reactor.netty.http.client.HttpClientRequest;
|
||||
import reactor.netty.resources.ConnectionProvider;
|
||||
import reactor.netty.resources.LoopResources;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Reactor-Netty implementation of {@link ClientHttpConnector}.
|
||||
*
|
||||
* <p>This class implements {@link Lifecycle} and can be optionally declared
|
||||
* as a Spring-managed bean.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
* @see reactor.netty.http.client.HttpClient
|
||||
*/
|
||||
public class ReactorClientHttpConnector implements ClientHttpConnector {
|
||||
public class ReactorClientHttpConnector implements ClientHttpConnector, SmartLifecycle {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ReactorClientHttpConnector.class);
|
||||
|
||||
private final static Function<HttpClient, HttpClient> defaultInitializer = client -> client.compress(true);
|
||||
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private HttpClient httpClient;
|
||||
|
||||
@Nullable
|
||||
private final ReactorResourceFactory resourceFactory;
|
||||
|
||||
@Nullable
|
||||
private final Function<HttpClient, HttpClient> mapper;
|
||||
|
||||
private volatile boolean running = true;
|
||||
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
@@ -54,6 +75,8 @@ public class ReactorClientHttpConnector implements ClientHttpConnector {
|
||||
*/
|
||||
public ReactorClientHttpConnector() {
|
||||
this.httpClient = defaultInitializer.apply(HttpClient.create());
|
||||
this.resourceFactory = null;
|
||||
this.mapper = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,14 +91,20 @@ public class ReactorClientHttpConnector implements ClientHttpConnector {
|
||||
* consider declaring a {@link ReactorResourceFactory} bean with
|
||||
* {@code globalResources=true} in order to ensure the Reactor Netty global
|
||||
* resources are shut down when the Spring ApplicationContext is closed.
|
||||
* @param factory the resource factory to obtain the resources from
|
||||
* @param resourceFactory the resource factory to obtain the resources from
|
||||
* @param mapper a mapper for further initialization of the created client
|
||||
* @since 5.1
|
||||
*/
|
||||
public ReactorClientHttpConnector(ReactorResourceFactory factory, Function<HttpClient, HttpClient> mapper) {
|
||||
ConnectionProvider provider = factory.getConnectionProvider();
|
||||
public ReactorClientHttpConnector(ReactorResourceFactory resourceFactory, Function<HttpClient, HttpClient> mapper) {
|
||||
this.httpClient = createHttpClient(resourceFactory, mapper);
|
||||
this.resourceFactory = resourceFactory;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
private static HttpClient createHttpClient(ReactorResourceFactory resourceFactory, Function<HttpClient, HttpClient> mapper) {
|
||||
ConnectionProvider provider = resourceFactory.getConnectionProvider();
|
||||
Assert.notNull(provider, "No ConnectionProvider: is ReactorResourceFactory not initialized yet?");
|
||||
this.httpClient = defaultInitializer.andThen(mapper).andThen(applyLoopResources(factory))
|
||||
return defaultInitializer.andThen(mapper).andThen(applyLoopResources(resourceFactory))
|
||||
.apply(HttpClient.create(provider));
|
||||
}
|
||||
|
||||
@@ -96,6 +125,8 @@ public class ReactorClientHttpConnector implements ClientHttpConnector {
|
||||
public ReactorClientHttpConnector(HttpClient httpClient) {
|
||||
Assert.notNull(httpClient, "HttpClient is required");
|
||||
this.httpClient = httpClient;
|
||||
this.resourceFactory = null;
|
||||
this.mapper = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,4 +162,52 @@ public class ReactorClientHttpConnector implements ClientHttpConnector {
|
||||
return new ReactorClientHttpRequest(method, uri, request, nettyOutbound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (!isRunning()) {
|
||||
if (this.resourceFactory != null && this.mapper != null) {
|
||||
this.httpClient = createHttpClient(this.resourceFactory, this.mapper);
|
||||
}
|
||||
else {
|
||||
logger.warn("Restarting a ReactorClientHttpConnector bean is only supported with externally managed Reactor Netty resources");
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (isRunning()) {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void stop(Runnable callback) {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
// Start after ReactorResourceFactory
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user