com.turbospaces.okhttp.OkHttpClientFactoryBean Maven / Gradle / Ivy
The newest version!
package com.turbospaces.okhttp;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.okhttp.interceptors.ProxyInterceptor;
import com.turbospaces.okhttp.interceptors.RemoveSoapHeadersInterceptor;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.okhttp3.OkHttpConnectionPoolMetrics;
import io.micrometer.core.instrument.binder.okhttp3.OkHttpMetricsEventListener;
import okhttp3.ConnectionPool;
import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
public class OkHttpClientFactoryBean extends AbstractFactoryBean implements BeanNameAware {
protected final ApplicationProperties props;
protected final MeterRegistry meterRegistry;
protected String beanName;
public OkHttpClientFactoryBean(ApplicationProperties props, MeterRegistry meterRegistry) {
this.props = Objects.requireNonNull(props);
this.meterRegistry = Objects.requireNonNull(meterRegistry);
setSingleton(true);
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public Class> getObjectType() {
return OkHttpClient.class;
}
public OkHttpClient.Builder getBuilder() {
var builder = new OkHttpClient.Builder();
var maxIdleConnections = props.TCP_CONNECTION_CLOSE_IDLE_IMMEDIATELY.get() ? 0 : 5; //add prop
ConnectionPool connectionPool = new ConnectionPool(
maxIdleConnections,
props.TCP_KEEP_ALIVE_TIMEOUT.get().toMillis(),
TimeUnit.MILLISECONDS
);
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(props.HTTP_POOL_MAX_SIZE.get());
dispatcher.setMaxRequestsPerHost(props.HTTP_POOL_MAX_PER_ROUTE.get());
builder.addInterceptor(new ProxyInterceptor(props))
.addInterceptor(new RemoveSoapHeadersInterceptor())
.retryOnConnectionFailure(true)
.connectionPool(connectionPool)
.dispatcher(dispatcher)
//The read timeout is applied to both the TCP socket and for individual read IO operations.
.readTimeout(props.TCP_SOCKET_TIMEOUT.get())
//Sets the default connect timeout for new connections when connecting a TCP socket to the target host
.connectTimeout(props.TCP_CONNECTION_TIMEOUT.get())
// ~ metrics
.eventListener(OkHttpMetricsEventListener
.builder(meterRegistry, "httpcomponents.httpclient.request")
.uriMapper(this::getUri)
.build());
new OkHttpConnectionPoolMetrics(connectionPool, "okhttp.pool", Collections.emptyList(), maxIdleConnections).bindTo(meterRegistry);
return builder;
}
@Override
protected OkHttpClient createInstance() throws Exception {
OkHttpClient.Builder builder = getBuilder();
return builder.build();
}
private String getUri(Request httpRequest) {
List mask = props.HTTP_METRICS_OUTBOUND_PATH_MASK.get();
String uriPattern = httpRequest.header("URI_PATTERN");
if (StringUtils.isNotEmpty(uriPattern)) {
return uriPattern;
}
String uri = httpRequest.url().uri().toString();
if (CollectionUtils.isNotEmpty(mask)) {
Optional mapping = mask.stream().filter(p -> p.asPredicate().test(uri)).map(Pattern::pattern).findAny();
if (mapping.isPresent()) {
return mapping.get();
}
}
return URI.create(uri).getPath();
}
}