Merge pull request #2080 from ryanjbaxter/http-client-centralization

Centralizing HTTP client creation and configuration
This commit is contained in:
Ryan Baxter
2017-08-03 13:02:09 -04:00
committed by GitHub
36 changed files with 1351 additions and 370 deletions

View File

@@ -11,6 +11,7 @@
include::intro.adoc[]
== Service Discovery: Eureka Clients
Service Discovery is one of the key tenets of a microservice based architecture. Trying to hand configure each client or some form of convention can be very difficult to do and can be very brittle. Eureka is the Netflix Service Discovery Server and Client. The server can be configured and deployed to be highly available, with each server replicating state about the registered services to the others.
@@ -1050,6 +1051,7 @@ Spring Cloud Netflix provides the following beans by default for feign (`BeanTyp
* `Client` feignClient: if Ribbon is enabled it is a `LoadBalancerFeignClient`, otherwise the default feign client is used.
The OkHttpClient and ApacheHttpClient feign clients can be used by setting `feign.okhttp.enabled` or `feign.httpclient.enabled` to `true`, respectively, and having them on the classpath.
You can customize the HTTP client used by providing a bean of either `ClosableHttpClient` when using Apache or `OkHttpClient` whe using OK HTTP.
Spring Cloud Netflix _does not_ provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:
@@ -1612,7 +1614,9 @@ path rendering the `users` path unreachable.
The default HTTP client used by zuul is now backed by the Apache HTTP Client instead of the
deprecated Ribbon `RestClient`. To use `RestClient` or to use the `okhttp3.OkHttpClient` set
`ribbon.restclient.enabled=true` or `ribbon.okhttp.enabled=true` respectively.
`ribbon.restclient.enabled=true` or `ribbon.okhttp.enabled=true` respectively. If you would
like to customize the Apache HTTP client or the OK HTTP client provide a bean of type
`ClosableHttpClient` or `OkHttpClient`.
=== Cookies and Sensitive Headers
@@ -2636,3 +2640,14 @@ method to determine whether you want to retry a request given the status code.
You can turn off Zuul's retry functionality by setting `zuul.retryable` to `false`. You
can also disable retry functionality on route by route basis by setting
`zuul.routes.routename.retryable` to `false`.
== HTTP Clients
Spring Cloud Netflix will automatically create the HTTP client used by Ribbon, Feign, and
Zuul for you. However you can also provide your own HTTP clients customized how you please
yourself. To do this you can either create a bean of type `ClosableHttpClient` if you
are using the Apache Http Cient, or `OkHttpClient` if you are using OK HTTP.
NOTE: When you create your own HTTP client you are also responsible for implementing
the correct connection management strategies for these clients. Doing this improperly
can result in resource management issues.

View File

@@ -18,8 +18,15 @@ package org.springframework.cloud.netflix.feign;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -27,6 +34,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -34,6 +46,9 @@ import feign.Client;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import javax.annotation.PreDestroy;
/**
* @author Spencer Gibb
@@ -41,7 +56,7 @@ import feign.okhttp.OkHttpClient;
*/
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
@Autowired(required = false)
@@ -86,38 +101,109 @@ public class FeignAutoConfiguration {
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private HttpClient httpClient;
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder().
setConnectionManager(httpClientConnectionManager).
setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient() {
if (this.httpClient != null) {
return new ApacheHttpClient(this.httpClient);
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
return new ApacheHttpClient();
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnProperty(value = "feign.okhttp.enabled", matchIfMissing = true)
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
@Autowired(required = false)
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(false).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient() {
if (this.okHttpClient != null) {
return new OkHttpClient(this.okHttpClient);
}
return new OkHttpClient();
return new OkHttpClient(this.okHttpClient);
}
}

View File

@@ -20,8 +20,10 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,6 +44,7 @@ import feign.Request;
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
@@ -61,7 +64,8 @@ public class FeignRibbonClientAutoConfiguration {
@Primary
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
SpringClientFactory factory, LoadBalancedRetryPolicyFactory retryPolicyFactory) {
SpringClientFactory factory,
LoadBalancedRetryPolicyFactory retryPolicyFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryPolicyFactory, true);
}
@@ -70,5 +74,4 @@ public class FeignRibbonClientAutoConfiguration {
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
}

View File

@@ -19,11 +19,22 @@ package org.springframework.cloud.netflix.feign.ribbon;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import java.util.Timer;
import java.util.TimerTask;
import javax.annotation.PreDestroy;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -36,19 +47,67 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
class HttpClientFeignLoadBalancedConfiguration {
@Autowired(required = false)
private HttpClient httpClient;
@Configuration
@ConditionalOnMissingBean(CloseableHttpClient.class)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
ApacheHttpClient delegate;
if (this.httpClient != null) {
delegate = new ApacheHttpClient(this.httpClient);
} else {
delegate = new ApacheHttpClient();
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder().
setDefaultRequestConfig(defaultRequestConfig).
setConnectionManager(httpClientConnectionManager).build();
return this.httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}

View File

@@ -19,10 +19,17 @@ package org.springframework.cloud.netflix.feign.ribbon;
import feign.Client;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -32,22 +39,50 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled", matchIfMissing = true)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {
@Autowired(required = false)
private okhttp3.OkHttpClient okHttpClient;
@Configuration
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(false).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
OkHttpClient delegate;
if (this.okHttpClient != null) {
delegate = new OkHttpClient(this.okHttpClient);
} else {
delegate = new OkHttpClient();
}
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}

View File

@@ -0,0 +1,110 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign.support;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Ryan Baxter
*/
@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {
public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
public static final int DEFAULT_MAX_CONNECTIONS = 200;
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
public static final long DEFAULT_TIME_TO_LIVE = 900L;
public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
private long timeToLive = DEFAULT_TIME_TO_LIVE;
private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
public int getConnectionTimerRepeat() {
return connectionTimerRepeat;
}
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
this.connectionTimerRepeat = connectionTimerRepeat;
}
public boolean isDisableSslValidation() {
return disableSslValidation;
}
public void setDisableSslValidation(boolean disableSslValidation) {
this.disableSslValidation = disableSslValidation;
}
public int getMaxConnections() {
return maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getMaxConnectionsPerRoute() {
return maxConnectionsPerRoute;
}
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
this.maxConnectionsPerRoute = maxConnectionsPerRoute;
}
public long getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(long timeToLive) {
this.timeToLive = timeToLive;
}
public TimeUnit getTimeToLiveUnit() {
return timeToLiveUnit;
}
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
this.timeToLiveUnit = timeToLiveUnit;
}
public boolean isFollowRedirects() {
return followRedirects;
}
public void setFollowRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.apache.HttpClientRibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonConfiguration;
import org.springframework.context.annotation.Bean;
@@ -64,7 +65,7 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
@Value("${ribbon.client.name}")
@@ -148,8 +149,8 @@ public class RibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public RibbonLoadBalancerContext ribbonLoadBalancerContext(
ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
IClientConfig config, RetryHandler retryHandler) {
return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}
@@ -158,7 +159,7 @@ public class RibbonClientConfiguration {
public RetryHandler retryHandler(IClientConfig config) {
return new DefaultLoadBalancerRetryHandler(config);
}
@Bean
@ConditionalOnMissingBean
public ServerIntrospector serverIntrospector() {
@@ -185,18 +186,16 @@ public class RibbonClientConfiguration {
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server);
URI uri = updateToHttpsIfNeeded(original, this.config,
this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
@Override
protected Client apacheHttpClientSpecificInitialization() {
ApacheHttpClient4 apache = (ApacheHttpClient4) super
.apacheHttpClientSpecificInitialization();
apache.getClientHandler()
.getHttpClient()
.getParams()
.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
ApacheHttpClient4 apache = (ApacheHttpClient4) super.apacheHttpClientSpecificInitialization();
apache.getClientHandler().getHttpClient().getParams().setParameter(
ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
return apache;
}

View File

@@ -17,18 +17,31 @@
package org.springframework.cloud.netflix.ribbon.apache;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.servo.monitor.Monitors;
@@ -43,14 +56,89 @@ public class HttpClientRibbonConfiguration {
@Value("${ribbon.client.name}")
private String name = "client";
@Configuration
protected static class ApacheHttpClientConfiguration {
private final Timer connectionManagerTimer = new Timer(
"RibbonApacheHttpClientConfiguration.connectionManagerTimer", true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager httpClientConnectionManager(
IClientConfig config,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory) {
Integer maxTotalConnections = config.getPropertyAsInteger(
CommonClientConfigKey.MaxTotalConnections,
DefaultClientConfigImpl.DEFAULT_MAX_TOTAL_CONNECTIONS);
Integer maxConnectionsPerHost = config.getPropertyAsInteger(
CommonClientConfigKey.MaxConnectionsPerHost,
DefaultClientConfigImpl.DEFAULT_MAX_CONNECTIONS_PER_HOST);
Integer timerRepeat = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectionCleanerRepeatInterval,
DefaultClientConfigImpl.DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS);
Object timeToLiveObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTime);
Long timeToLive = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME;
Object ttlUnitObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits);
TimeUnit ttlUnit = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
if (timeToLiveObj instanceof Long) {
timeToLive = (Long) timeToLiveObj;
}
if (ttlUnitObj instanceof TimeUnit) {
ttlUnit = (TimeUnit) ttlUnitObj;
}
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, maxTotalConnections,
maxConnectionsPerHost, timeToLive, ttlUnit, registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, timerRepeat);
return connectionManager;
}
@Bean
@ConditionalOnMissingBean(CloseableHttpClient.class)
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager connectionManager, IClientConfig config) {
Boolean followRedirects = config.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
Integer connectTimeout = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
DefaultClientConfigImpl.DEFAULT_CONNECT_TIMEOUT);
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setRedirectsEnabled(followRedirects).build();
this.httpClient = httpClientFactory.createBuilder().
setDefaultRequestConfig(defaultRequestConfig).
setConnectionManager(connectionManager).build();
return httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler) {
ILoadBalancer loadBalancer, RetryHandler retryHandler, CloseableHttpClient httpClient) {
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(
config, serverIntrospector);
httpClient, config, serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
@@ -63,9 +151,9 @@ public class HttpClientRibbonConfiguration {
public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory, CloseableHttpClient httpClient) {
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
config, serverIntrospector, loadBalancedRetryPolicyFactory);
httpClient, config, serverIntrospector, loadBalancedRetryPolicyFactory);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);

View File

@@ -22,6 +22,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
@@ -47,68 +48,94 @@ import com.netflix.loadbalancer.Server;
* An Apache HTTP client which leverages Spring Retry to retry failed requests.
* @author Ryan Baxter
*/
public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient implements ServiceInstanceChooser {
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory =
new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
public RetryableRibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient
implements ServiceInstanceChooser {
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
public RetryableRibbonLoadBalancingHttpClient(IClientConfig config,
ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
public RetryableRibbonLoadBalancingHttpClient(CloseableHttpClient delegate,
IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
@Override
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request,
final IClientConfig configOverride) throws Exception {
final RequestConfig.Builder builder = RequestConfig.custom();
IClientConfig config = configOverride != null ? configOverride : this.config;
builder.setConnectTimeout(config.get(
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setSocketTimeout(config.get(
CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(config.get(
CommonClientConfigKey.FollowRedirects, this.followRedirects));
builder.setConnectTimeout(
config.get(CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setSocketTimeout(
config.get(CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(
config.get(CommonClientConfigKey.FollowRedirects, this.followRedirects));
final RequestConfig requestConfig = builder.build();
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory
.create(this.getClientName(), this);
RetryCallback retryCallback = new RetryCallback() {
@Override
public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
//on retries the policy will choose the server and set it in the context
//extract the server and update the request being made
public RibbonApacheHttpResponse doWithRetry(RetryContext context)
throws Exception {
// on retries the policy will choose the server and set it in the context
// extract the server and update the request being made
RibbonApacheHttpRequest newRequest = request;
if(context instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
if(service != null) {
//Reconstruct the request URI using the host and port set in the retry context
newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
if (context instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext) context)
.getServiceInstance();
if (service != null) {
// Reconstruct the request URI using the host and port set in the
// retry context
newRequest = newRequest.withNewUri(new URI(
service.getUri().getScheme(),
newRequest.getURI().getUserInfo(), service.getHost(),
service.getPort(), newRequest.getURI().getPath(),
newRequest.getURI().getQuery(),
newRequest.getURI().getFragment()));
}
}
if (isSecure(configOverride)) {
final URI secureUri = UriComponentsBuilder.fromUri(newRequest.getUri())
.scheme("https").build().toUri();
final URI secureUri = UriComponentsBuilder
.fromUri(newRequest.getUri()).scheme("https").build().toUri();
newRequest = newRequest.withNewUri(secureUri);
}
HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
if(retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
if(CloseableHttpResponse.class.isInstance(httpResponse)) {
((CloseableHttpResponse)httpResponse).close();
final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate
.execute(httpUriRequest);
if (retryPolicy.retryableStatusCode(
httpResponse.getStatusLine().getStatusCode())) {
if (CloseableHttpResponse.class.isInstance(httpResponse)) {
((CloseableHttpResponse) httpResponse).close();
}
throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName,
throw new RetryableStatusCodeException(
RetryableRibbonLoadBalancingHttpClient.this.clientName,
httpResponse.getStatusLine().getStatusCode());
}
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
return new RibbonApacheHttpResponse(httpResponse,
httpUriRequest.getURI());
}
};
return this.executeWithRetry(request, retryPolicy, retryCallback);
}
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, LoadBalancedRetryPolicy retryPolicy, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request,
LoadBalancedRetryPolicy retryPolicy,
RetryCallback<RibbonApacheHttpResponse, IOException> callback)
throws Exception {
RetryTemplate retryTemplate = new RetryTemplate();
boolean retryable = request.getContext() == null ? true :
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
retryTemplate.setRetryPolicy(retryPolicy == null || !retryable ? new NeverRetryPolicy()
boolean retryable = request.getContext() == null ? true
: BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(),
true);
retryTemplate.setRetryPolicy(retryPolicy == null || !retryable
? new NeverRetryPolicy()
: new RetryPolicy(request, retryPolicy, this, this.getClientName()));
return retryTemplate.execute(callback);
}
@@ -116,17 +143,18 @@ public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingH
@Override
public ServiceInstance choose(String serviceId) {
Server server = this.getLoadBalancer().chooseServer(serviceId);
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
server);
return new RibbonLoadBalancerClient.RibbonServer(serviceId, server);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
RibbonApacheHttpRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
}
static class RetryPolicy extends FeignRetryPolicy {
public RetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
public RetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy,
ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
super(request, policy, serviceInstanceChooser, serviceName);
}
}

View File

@@ -20,12 +20,11 @@ import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
@@ -39,36 +38,29 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
* @author Christian Lohmann
* @author Ryan Baxter
*/
//TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient
// TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient
public class RibbonLoadBalancingHttpClient extends
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> {
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, CloseableHttpClient> {
@Deprecated
public RibbonLoadBalancingHttpClient() {
super();
}
@Deprecated
public RibbonLoadBalancingHttpClient(final ILoadBalancer lb) {
super(lb);
}
public RibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector) {
public RibbonLoadBalancingHttpClient(IClientConfig config,
ServerIntrospector serverIntrospector) {
super(config, serverIntrospector);
}
public RibbonLoadBalancingHttpClient(HttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector) {
public RibbonLoadBalancingHttpClient(CloseableHttpClient delegate,
IClientConfig config, ServerIntrospector serverIntrospector) {
super(delegate, config, serverIntrospector);
}
protected HttpClient createDelegate(IClientConfig config) {
protected CloseableHttpClient createDelegate(IClientConfig config) {
return HttpClientBuilder.create()
// already defaults to 0 in builder, so resetting to 0 won't hurt
.setMaxConnTotal(config.getPropertyAsInteger(CommonClientConfigKey.MaxTotalConnections, 0))
.setMaxConnTotal(config.getPropertyAsInteger(
CommonClientConfigKey.MaxTotalConnections, 0))
// already defaults to 0 in builder, so resetting to 0 won't hurt
.setMaxConnPerRoute(config.getPropertyAsInteger(CommonClientConfigKey.MaxConnectionsPerHost, 0))
.disableCookieManagement()
.useSystemProperties() // for proxy
.setMaxConnPerRoute(config.getPropertyAsInteger(
CommonClientConfigKey.MaxConnectionsPerHost, 0))
.disableCookieManagement().useSystemProperties() // for proxy
.build();
}
@@ -77,12 +69,12 @@ public class RibbonLoadBalancingHttpClient extends
final IClientConfig configOverride) throws Exception {
final RequestConfig.Builder builder = RequestConfig.custom();
IClientConfig config = configOverride != null ? configOverride : this.config;
builder.setConnectTimeout(config.get(
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setSocketTimeout(config.get(
CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(config.get(
CommonClientConfigKey.FollowRedirects, this.followRedirects));
builder.setConnectTimeout(
config.get(CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setSocketTimeout(
config.get(CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(
config.get(CommonClientConfigKey.FollowRedirects, this.followRedirects));
final RequestConfig requestConfig = builder.build();
if (isSecure(configOverride)) {
@@ -97,13 +89,15 @@ public class RibbonLoadBalancingHttpClient extends
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server);
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector,
server);
return super.reconstructURIWithServer(server, uri);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false,
RetryHandler.DEFAULT, requestConfig);
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
RibbonApacheHttpRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT,
requestConfig);
}
}

View File

@@ -41,16 +41,6 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
public class OkHttpLoadBalancingClient
extends AbstractLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {
@Deprecated
public OkHttpLoadBalancingClient() {
super();
}
@Deprecated
public OkHttpLoadBalancingClient(final ILoadBalancer lb) {
super(lb);
}
public OkHttpLoadBalancingClient(IClientConfig config,
ServerIntrospector serverIntrospector) {
super(config, serverIntrospector);

View File

@@ -17,8 +17,15 @@
package org.springframework.cloud.netflix.ribbon.okhttp;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.servo.monitor.Monitors;
@@ -28,6 +35,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,6 +51,61 @@ public class OkHttpRibbonConfiguration {
@Value("${ribbon.client.name}")
private String name = "client";
@Configuration
protected static class OkHttpClientConfiguration {
private OkHttpClient httpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(IClientConfig config,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = config.getPropertyAsInteger(
CommonClientConfigKey.MaxTotalConnections,
DefaultClientConfigImpl.DEFAULT_MAX_TOTAL_CONNECTIONS);
Object timeToLiveObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTime);
Long timeToLive = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME;
Object ttlUnitObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits);
TimeUnit ttlUnit = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
if (timeToLiveObj instanceof Long) {
timeToLive = (Long) timeToLiveObj;
}
if (ttlUnitObj instanceof TimeUnit) {
ttlUnit = (TimeUnit) ttlUnitObj;
}
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
@ConditionalOnMissingBean(OkHttpClient.class)
public OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, IClientConfig config) {
Boolean followRedirects = config.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
Integer connectTimeout = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
DefaultClientConfigImpl.DEFAULT_CONNECT_TIMEOUT);
Integer readTimeout = config.getPropertyAsInteger(CommonClientConfigKey.ReadTimeout,
DefaultClientConfigImpl.DEFAULT_READ_TIMEOUT);
this.httpClient = httpClientFactory.createBuilder(false).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
readTimeout(readTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.httpClient;
}
@PreDestroy
public void destroy() {
if(httpClient != null) {
httpClient.dispatcher().executorService().shutdown();
httpClient.connectionPool().evictAll();
}
}
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@@ -50,8 +114,9 @@ public class OkHttpRibbonConfiguration {
ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer,
RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(config,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
OkHttpClient delegate) {
RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(delegate, config,
serverIntrospector, loadBalancedRetryPolicyFactory);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
@@ -64,8 +129,8 @@ public class OkHttpRibbonConfiguration {
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public OkHttpLoadBalancingClient retryableOkHttpLoadBalancingClient(IClientConfig config,
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
RetryHandler retryHandler) {
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config,
RetryHandler retryHandler, OkHttpClient delegate) {
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(delegate, config,
serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);

View File

@@ -49,9 +49,9 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
public RetryableOkHttpLoadBalancingClient(IClientConfig config, ServerIntrospector serverIntrospector,
public RetryableOkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(config, serverIntrospector);
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}

View File

@@ -105,10 +105,6 @@ public class RibbonCommandFactoryConfiguration {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty(name = "zuul.ribbon.httpclient.enabled", matchIfMissing = true)
static class ZuulProperty {}
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
static class RibbonProperty {}
}
@@ -124,10 +120,6 @@ public class RibbonCommandFactoryConfiguration {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("zuul.ribbon.okhttp.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.okhttp.enabled")
static class RibbonProperty {}
}
@@ -143,10 +135,6 @@ public class RibbonCommandFactoryConfiguration {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("zuul.ribbon.restclient.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.restclient.enabled")
static class RibbonProperty {}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.zuul;
import java.util.Collections;
import java.util.List;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.trace.TraceRepository;
@@ -32,6 +33,9 @@ import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
@@ -59,7 +63,8 @@ import org.springframework.context.annotation.Import;
@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class })
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
@@ -75,35 +80,51 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
@Override
public HasFeatures zuulFeature() {
return HasFeatures.namedFeature("Zuul (Discovery)", ZuulProxyAutoConfiguration.class);
return HasFeatures.namedFeature("Zuul (Discovery)",
ZuulProxyAutoConfiguration.class);
}
@Bean
@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
public DiscoveryClientRouteLocator discoveryRouteLocator() {
return new DiscoveryClientRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties,
this.serviceRouteMapper);
return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
this.discovery, this.zuulProperties, this.serviceRouteMapper);
}
// pre filters
@Bean
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties,
proxyRequestHelper);
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),
this.zuulProperties, proxyRequestHelper);
}
// route filters
@Bean
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
RibbonCommandFactory<?> ribbonCommandFactory) {
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
this.requestCustomizers);
return filter;
}
@Bean
@ConditionalOnMissingBean(SimpleHostRoutingFilter.class)
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(helper, zuulProperties);
@ConditionalOnMissingBean({SimpleHostRoutingFilter.class, CloseableHttpClient.class})
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
return new SimpleHostRoutingFilter(helper, zuulProperties,
connectionManagerFactory, httpClientFactory);
}
@Bean
@ConditionalOnMissingBean({SimpleHostRoutingFilter.class})
public SimpleHostRoutingFilter simpleHostRoutingFilter2(ProxyRequestHelper helper,
ZuulProperties zuulProperties,
CloseableHttpClient httpClient) {
return new SimpleHostRoutingFilter(helper, zuulProperties,
httpClient);
}
@Bean
@@ -144,7 +165,8 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
}
@Bean
public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator, RoutesEndpoint endpoint) {
public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator,
RoutesEndpoint endpoint) {
return new RoutesMvcEndpoint(endpoint, routeLocator);
}
@@ -160,7 +182,8 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
}
}
private static class ZuulDiscoveryRefreshListener implements ApplicationListener<ApplicationEvent> {
private static class ZuulDiscoveryRefreshListener
implements ApplicationListener<ApplicationEvent> {
private HeartbeatMonitor monitor = new HeartbeatMonitor();

View File

@@ -19,9 +19,6 @@ package org.springframework.cloud.netflix.zuul.filters.route;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -30,9 +27,6 @@ import java.util.TimerTask;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
@@ -41,32 +35,21 @@ import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HttpContext;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
@@ -80,14 +63,12 @@ import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.HTTPS_SCHEME;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.HTTP_SCHEME;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SIMPLE_HOST_ROUTING_FILTER_ORDER;
/**
* Route {@link ZuulFilter} that sends requests to predetermined URLs via apache {@link HttpClient}.
* URLs are found in {@link RequestContext#getRouteHost()}.
* Route {@link ZuulFilter} that sends requests to predetermined URLs via apache
* {@link HttpClient}. URLs are found in {@link RequestContext#getRouteHost()}.
*
* @author Spencer Gibb
* @author Dave Syer
@@ -105,51 +86,78 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
private ProxyRequestHelper helper;
private Host hostProperties;
private PoolingHttpClientConnectionManager connectionManager;
private ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
private ApacheHttpClientFactory httpClientFactory;
private HttpClientConnectionManager connectionManager;
private CloseableHttpClient httpClient;
private boolean customHttpClient = false;
@EventListener
public void onPropertyChange(EnvironmentChangeEvent event) {
boolean createNewClient = false;
if(!customHttpClient) {
boolean createNewClient = false;
for (String key : event.getKeys()) {
if (key.startsWith("zuul.host.")) {
createNewClient = true;
break;
for (String key : event.getKeys()) {
if (key.startsWith("zuul.host.")) {
createNewClient = true;
break;
}
}
}
if (createNewClient) {
try {
SimpleHostRoutingFilter.this.httpClient.close();
if (createNewClient) {
try {
SimpleHostRoutingFilter.this.httpClient.close();
} catch (IOException ex) {
log.error("error closing client", ex);
}
SimpleHostRoutingFilter.this.httpClient = newClient();
}
catch (IOException ex) {
log.error("error closing client", ex);
}
SimpleHostRoutingFilter.this.httpClient = newClient();
}
}
public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties) {
public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
this.helper = helper;
this.hostProperties = properties.getHost();
this.sslHostnameValidationEnabled = properties.isSslHostnameValidationEnabled();
this.forceOriginalQueryStringEncoding = properties
.isForceOriginalQueryStringEncoding();
this.connectionManagerFactory = connectionManagerFactory;
this.httpClientFactory = httpClientFactory;
}
public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties,
CloseableHttpClient httpClient) {
this.helper = helper;
this.hostProperties = properties.getHost();
this.sslHostnameValidationEnabled = properties.isSslHostnameValidationEnabled();
this.forceOriginalQueryStringEncoding = properties
.isForceOriginalQueryStringEncoding();
this.httpClient = httpClient;
this.customHttpClient = true;
}
@PostConstruct
private void initialize() {
this.httpClient = newClient();
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
if (SimpleHostRoutingFilter.this.connectionManager == null) {
return;
if(!customHttpClient) {
this.connectionManager = connectionManagerFactory.newConnectionManager(
this.sslHostnameValidationEnabled,
this.hostProperties.getMaxTotalConnections(),
this.hostProperties.getMaxPerRouteConnections(),
this.hostProperties.getTimeToLive(), this.hostProperties.getTimeUnit(),
null);
this.httpClient = newClient();
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
if (SimpleHostRoutingFilter.this.connectionManager == null) {
return;
}
SimpleHostRoutingFilter.this.connectionManager.closeExpiredConnections();
}
SimpleHostRoutingFilter.this.connectionManager.closeExpiredConnections();
}
}, 30000, 5000);
}, 30000, 5000);
}
}
@PreDestroy
@@ -201,50 +209,8 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
return null;
}
protected PoolingHttpClientConnectionManager newConnectionManager() {
try {
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates,
String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates,
String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
} }, new SecureRandom());
RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder
.<ConnectionSocketFactory> create()
.register(HTTP_SCHEME, PlainConnectionSocketFactory.INSTANCE);
if (this.sslHostnameValidationEnabled) {
registryBuilder.register(HTTPS_SCHEME,
new SSLConnectionSocketFactory(sslContext));
}
else {
registryBuilder.register(HTTPS_SCHEME, new SSLConnectionSocketFactory(
sslContext, NoopHostnameVerifier.INSTANCE));
}
final Registry<ConnectionSocketFactory> registry = registryBuilder.build();
this.connectionManager = new PoolingHttpClientConnectionManager(registry, null, null, null,
hostProperties.getTimeToLive(), hostProperties.getTimeUnit());
this.connectionManager
.setMaxTotal(this.hostProperties.getMaxTotalConnections());
this.connectionManager.setDefaultMaxPerRoute(
this.hostProperties.getMaxPerRouteConnections());
return this.connectionManager;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
protected HttpClientConnectionManager getConnectionManager() {
return connectionManager;
}
protected CloseableHttpClient newClient() {
@@ -252,30 +218,9 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
.setSocketTimeout(this.hostProperties.getSocketTimeoutMillis())
.setConnectTimeout(this.hostProperties.getConnectTimeoutMillis())
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
HttpClientBuilder httpClientBuilder = HttpClients.custom();
if (!this.sslHostnameValidationEnabled) {
httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
return httpClientBuilder.setConnectionManager(newConnectionManager())
.disableContentCompression()
.useSystemProperties().setDefaultRequestConfig(requestConfig)
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
.setRedirectStrategy(new RedirectStrategy() {
@Override
public boolean isRedirected(HttpRequest request,
HttpResponse response, HttpContext context)
throws ProtocolException {
return false;
}
@Override
public HttpUriRequest getRedirect(HttpRequest request,
HttpResponse response, HttpContext context)
throws ProtocolException {
return null;
}
}).build();
return httpClientFactory.createBuilder().
setDefaultRequestConfig(requestConfig).
setConnectionManager(this.connectionManager).build();
}
private CloseableHttpResponse forward(CloseableHttpClient httpclient, String verb,
@@ -295,9 +240,11 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
contentType = ContentType.parse(request.getContentType());
}
InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength, contentType);
InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength,
contentType);
HttpRequest httpRequest = buildHttpRequest(verb, uri, entity, headers, params, request);
HttpRequest httpRequest = buildHttpRequest(verb, uri, entity, headers, params,
request);
try {
log.debug(httpHost.getHostName() + " " + httpHost.getPort() + " "
+ httpHost.getSchemeName());
@@ -323,30 +270,30 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
? getEncodedQueryString(request) : this.helper.getQueryString(params));
switch (verb.toUpperCase()) {
case "POST":
HttpPost httpPost = new HttpPost(uriWithQueryString);
httpRequest = httpPost;
httpPost.setEntity(entity);
break;
case "PUT":
HttpPut httpPut = new HttpPut(uriWithQueryString);
httpRequest = httpPut;
httpPut.setEntity(entity);
break;
case "PATCH":
HttpPatch httpPatch = new HttpPatch(uriWithQueryString);
httpRequest = httpPatch;
httpPatch.setEntity(entity);
break;
case "DELETE":
BasicHttpEntityEnclosingRequest entityRequest = new BasicHttpEntityEnclosingRequest(
verb, uriWithQueryString);
httpRequest = entityRequest;
entityRequest.setEntity(entity);
break;
default:
httpRequest = new BasicHttpRequest(verb, uriWithQueryString);
log.debug(uriWithQueryString);
case "POST":
HttpPost httpPost = new HttpPost(uriWithQueryString);
httpRequest = httpPost;
httpPost.setEntity(entity);
break;
case "PUT":
HttpPut httpPut = new HttpPut(uriWithQueryString);
httpRequest = httpPut;
httpPut.setEntity(entity);
break;
case "PATCH":
HttpPatch httpPatch = new HttpPatch(uriWithQueryString);
httpRequest = httpPatch;
httpPatch.setEntity(entity);
break;
case "DELETE":
BasicHttpEntityEnclosingRequest entityRequest = new BasicHttpEntityEnclosingRequest(
verb, uriWithQueryString);
httpRequest = entityRequest;
entityRequest.setEntity(entity);
break;
default:
httpRequest = new BasicHttpRequest(verb, uriWithQueryString);
log.debug(uriWithQueryString);
}
httpRequest.setHeaders(convertHeaders(headers));

View File

@@ -0,0 +1,162 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
import feign.Client;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockingDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.DefaultOkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.DefaultOkHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommand;
import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = OkHttpClientConfigurationTestApp.class, value = {"feign.okhttp.enabled: true",
"spring.cloud.httpclientfactories.ok.enabled: true", "ribbon.eureka.enabled = false", "ribbon.okhttp.enabled: true",
"feign.okhttp.enabled: true", "ribbon.httpclient.enabled: false", "feign.httpclient.enabled: false"})
@DirtiesContext
public class OkHttpClientConfigurationTests {
@Autowired
OkHttpClientFactory okHttpClientFactory;
@Autowired
OkHttpClientConnectionPoolFactory connectionPoolFactory;
@Autowired
LoadBalancerFeignClient feignClient;
@Autowired
OkHttpRibbonCommandFactory okHttpRibbonCommandFactory;
@Test
public void testFactories() {
Assertions.assertThat(connectionPoolFactory).isInstanceOf(OkHttpClientConnectionPoolFactory.class);
Assertions.assertThat(connectionPoolFactory).isInstanceOf(OkHttpClientConfigurationTestApp.MyOkHttpClientConnectionPoolFactory.class);
Assertions.assertThat(okHttpClientFactory).isInstanceOf(OkHttpClientFactory.class);
Assertions.assertThat(okHttpClientFactory).isInstanceOf(OkHttpClientConfigurationTestApp.MyOkHttpClientFactory.class);
}
@Test
public void testHttpClientWithFeign() {
Client delegate = feignClient.getDelegate();
assertTrue(feign.okhttp.OkHttpClient.class.isInstance(delegate));
feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient)delegate;
OkHttpClient httpClient = getField(okHttpClient, "delegate");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
@Test
public void testOkHttpLoadBalancingHttpClient() {
RibbonCommandContext context = new RibbonCommandContext("foo"," GET", "http://localhost",
false, new LinkedMultiValueMap<String, String>(), new LinkedMultiValueMap<String, String>(),
null, new ArrayList<RibbonRequestCustomizer>(), 0l);
OkHttpRibbonCommand command = okHttpRibbonCommandFactory.create(context);
OkHttpLoadBalancingClient ribbonClient = command.getClient();
OkHttpClient httpClient = getField(ribbonClient, "delegate");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
protected <T> T getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return (T)value;
}
}
@Configuration
@EnableAutoConfiguration
@RestController
@EnableZuulProxy
class OkHttpClientConfigurationTestApp {
@RequestMapping
public String index() {
return "hello";
}
static class MyOkHttpClientConnectionPoolFactory extends DefaultOkHttpClientConnectionPoolFactory {
@Override
public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
return new ConnectionPool();
}
}
static class MyOkHttpClientFactory extends DefaultOkHttpClientFactory {
}
@Configuration
static class MyConfig {
@Bean
public OkHttpClientConnectionPoolFactory connectionPoolFactory() {
return new MyOkHttpClientConnectionPoolFactory();
}
@Bean
public OkHttpClientFactory clientFactory() {
return new MyOkHttpClientFactory();
}
@Bean
public OkHttpClient client() {
return mock(OkHttpClient.class);
}
}
@FeignClient(name="foo", serviceId = "foo")
static interface FooClient {}
}

View File

@@ -0,0 +1,203 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.http.Header;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockingDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommand;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ApacheHttpClientConfigurationTestApp.class, value = {"feign.okhttp.enabled: false",
"ribbon.eureka.enabled = false"})
@DirtiesContext
public class ApacheHttpClientConfigurationTests {
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
ApacheHttpClientFactory httpClientFactory;
@Autowired
SimpleHostRoutingFilter simpleHostRoutingFilter;
@Autowired
LoadBalancerFeignClient feignClient;
@Autowired
HttpClientRibbonCommandFactory httpClientRibbonCommandFactory;
@Test
public void testFactories() {
Assertions.assertThat(connectionManagerFactory).isInstanceOf(ApacheHttpClientConnectionManagerFactory.class);
Assertions.assertThat(connectionManagerFactory).isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientConnectionManagerFactory.class);
Assertions.assertThat(httpClientFactory).isInstanceOf(ApacheHttpClientFactory.class);
Assertions.assertThat(httpClientFactory).isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientFactory.class);
}
@Test
public void testHttpClientSimpleHostRoutingFilter() {
CloseableHttpClient httpClient = getField(simpleHostRoutingFilter, "httpClient");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
@Test
public void testHttpClientWithFeign() {
Client delegate = feignClient.getDelegate();
assertTrue(ApacheHttpClient.class.isInstance(delegate));
ApacheHttpClient apacheHttpClient = (ApacheHttpClient)delegate;
HttpClient httpClient = getField(apacheHttpClient, "client");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
@Test
public void testRibbonLoadBalancingHttpClient() {
RibbonCommandContext context = new RibbonCommandContext("foo"," GET", "http://localhost",
false, new LinkedMultiValueMap<String, String>(), new LinkedMultiValueMap<String, String>(),
null, new ArrayList<RibbonRequestCustomizer>(), 0l);
HttpClientRibbonCommand command = httpClientRibbonCommandFactory.create(context);
RibbonLoadBalancingHttpClient ribbonClient = command.getClient();
CloseableHttpClient httpClient = getField(ribbonClient, "delegate");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
protected <T> T getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return (T)value;
}
}
@Configuration
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = {ApacheHttpClientConfigurationTestApp.FooClient.class})
@EnableZuulProxy
class ApacheHttpClientConfigurationTestApp {
@RequestMapping
public String index() {
return "hello";
}
static class MyApacheHttpClientConnectionManagerFactory extends DefaultApacheHttpClientConnectionManagerFactory {
@Override
public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, RegistryBuilder registry) {
return mock(PoolingHttpClientConnectionManager.class);
}
}
static class MyApacheHttpClientFactory extends DefaultApacheHttpClientFactory {
@Override
public HttpClientBuilder createBuilder() {
CloseableHttpClient client = mock(CloseableHttpClient.class);
CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
Header[] headers = new BasicHeader[0];
doReturn(headers).when(response).getAllHeaders();
try {
doReturn(response).when(client).execute(any(HttpUriRequest.class));
} catch (IOException e) {
e.printStackTrace();
}
HttpClientBuilder builder = mock(HttpClientBuilder.class);
doReturn(client).when(builder).build();
return builder;
}
}
@Configuration
static class MyConfig {
@Bean
public ApacheHttpClientFactory apacheHttpClientFactory() {
return new MyApacheHttpClientFactory();
}
@Bean
public ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
return new MyApacheHttpClientConnectionManagerFactory();
}
}
@FeignClient(name="foo", serviceId = "foo")
static interface FooClient {}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.ClassPathExclusions;
import org.springframework.cloud.FilteredClassPathRunner;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.feign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
import org.springframework.cloud.netflix.feign.encoding.FeignAcceptGzipEncodingInterceptor;
@@ -58,7 +59,7 @@ public class FeignCompressionTests {
context = new SpringApplicationBuilder().properties("feign.compression.response.enabled=true",
"feign.compression.request.enabled=true", "feign.okhttp.enabled=false").sources(PropertyPlaceholderAutoConfiguration.class,
ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class, PlainConfig.class, FeignContentGzipEncodingAutoConfiguration.class,
FeignAcceptGzipEncodingAutoConfiguration.class).web(false).run();
FeignAcceptGzipEncodingAutoConfiguration.class, HttpClientConfiguration.class).web(false).run();
}
@After

View File

@@ -0,0 +1,98 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign.support;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class FeignHttpClientPropertiesTests {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@After
public void clear() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testDefaults() {
setupContext();
assertEquals(FeignHttpClientProperties.DEFAULT_CONNECTION_TIMEOUT, getProperties().getConnectionTimeout());
assertEquals(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS, getProperties().getMaxConnections());
assertEquals(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS_PER_ROUTE, getProperties().getMaxConnectionsPerRoute());
assertEquals(FeignHttpClientProperties.DEFAULT_TIME_TO_LIVE, getProperties().getTimeToLive());
assertEquals(FeignHttpClientProperties.DEFAULT_DISABLE_SSL_VALIDATION, getProperties().isDisableSslValidation());
assertEquals(FeignHttpClientProperties.DEFAULT_FOLLOW_REDIRECTS, getProperties().isFollowRedirects());
}
@Test
public void testCustomization() {
addEnvironment(this.context, "feign.httpclient.maxConnections=2",
"feign.httpclient.connectionTimeout=2",
"feign.httpclient.maxConnectionsPerRoute=2",
"feign.httpclient.timeToLive=2",
"feign.httpclient.disableSslValidation=true",
"feign.httpclient.followRedirects=false");
setupContext();
assertEquals(2, getProperties().getMaxConnections());
assertEquals(2, getProperties().getConnectionTimeout());
assertEquals(2, getProperties().getMaxConnectionsPerRoute());
assertEquals(2L, getProperties().getTimeToLive());
assertTrue(getProperties().isDisableSslValidation());
assertFalse(getProperties().isFollowRedirects());
}
private void setupContext() {
this.context.register(PropertyPlaceholderAutoConfiguration.class, TestConfiguration.class);
this.context.refresh();
}
private FeignHttpClientProperties getProperties() {
return this.context.getBean(FeignHttpClientProperties.class);
}
@Configuration
@EnableConfigurationProperties
protected static class TestConfiguration {
@Bean
FeignHttpClientProperties zuulProperties() {
return new FeignHttpClientProperties() ;
}
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.feign.valid;
import org.junit.Test;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
import org.springframework.cloud.netflix.feign.FeignClient;
@@ -45,7 +46,7 @@ public class FeignClientValidationTests {
}
@Configuration
@Import(FeignAutoConfiguration.class)
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = GoodUrlConfiguration.Client.class)
protected static class GoodUrlConfiguration {
@@ -67,7 +68,7 @@ public class FeignClientValidationTests {
}
@Configuration
@Import(FeignAutoConfiguration.class)
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = PlaceholderUrlConfiguration.Client.class)
protected static class PlaceholderUrlConfiguration {
@@ -92,7 +93,7 @@ public class FeignClientValidationTests {
}
@Configuration
@Import(FeignAutoConfiguration.class)
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = GoodServiceIdConfiguration.Client.class)
protected static class GoodServiceIdConfiguration {

View File

@@ -63,7 +63,8 @@ import lombok.NoArgsConstructor;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignOkHttpTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
"spring.application.name=feignclienttest", "feign.hystrix.enabled=false",
"feign.httpclient.enabled=false", "feign.okhttp.enabled=true" })
"feign.httpclient.enabled=false", "feign.okhttp.enabled=true",
"spring.cloud.httpclientfactories.ok.enabled=true" })
@DirtiesContext
public class FeignOkHttpTests {

View File

@@ -28,6 +28,7 @@ import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration.OverrideRestClient;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
@@ -152,29 +153,29 @@ public class RibbonClientConfigurationTests {
@Test
public void testDefaultsToApacheHttpClient() {
testClient(RibbonLoadBalancingHttpClient.class, null, RestClient.class, OkHttpLoadBalancingClient.class);
testClient(RibbonLoadBalancingHttpClient.class, "ribbon.httpclient.enabled", RestClient.class, OkHttpLoadBalancingClient.class);
testClient(RibbonLoadBalancingHttpClient.class, new String[]{"ribbon.httpclient.enabled"}, RestClient.class, OkHttpLoadBalancingClient.class);
}
@SuppressWarnings("deprecation")
@Test
public void testEnableRestClient() {
testClient(RestClient.class, "ribbon.restclient.enabled", RibbonLoadBalancingHttpClient.class,
testClient(RestClient.class, new String[]{"ribbon.restclient.enabled"}, RibbonLoadBalancingHttpClient.class,
OkHttpLoadBalancingClient.class);
}
@SuppressWarnings("deprecation")
@Test
public void testEnableOkHttpClient() {
testClient(OkHttpLoadBalancingClient.class, "ribbon.okhttp.enabled", RibbonLoadBalancingHttpClient.class,
testClient(OkHttpLoadBalancingClient.class, new String[]{"ribbon.okhttp.enabled"}, RibbonLoadBalancingHttpClient.class,
RestClient.class);
}
void testClient(Class<?> clientType, String property, Class<?>... excludedTypes) {
void testClient(Class<?> clientType, String[] properties, Class<?>... excludedTypes) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(RibbonAutoConfiguration.class,
context.register(HttpClientConfiguration.class, RibbonAutoConfiguration.class,
RibbonClientConfiguration.class);
if (property != null) {
EnvironmentTestUtils.addEnvironment(context, property);
if (properties != null) {
EnvironmentTestUtils.addEnvironment(context, properties);
}
context.refresh();
context.getBean(clientType);

View File

@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.test.TestLoadBalancer;
@@ -102,7 +103,7 @@ public class RibbonClientPreprocessorPropertiesOverridesIntegrationTests {
@Configuration
@RibbonClients
@Import({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
@Import({ UtilAutoConfiguration.class, HttpClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class })
protected static class TestConfiguration {
}

View File

@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientsPreprocessorIntegrationTests.TestConfiguration;
@@ -72,7 +73,7 @@ public class RibbonClientsPreprocessorIntegrationTests {
@Configuration
@RibbonClients(@RibbonClient(name = "foo", configuration = FooConfiguration.class))
@Import({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class })
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class, HttpClientConfiguration.class})
protected static class TestConfiguration {
}

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.ribbon;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.junit.Test;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -69,7 +70,7 @@ public class SpringClientFactoryTests {
public void testConfigureRetry() {
SpringClientFactory factory = new SpringClientFactory();
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(
RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class);
RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class, HttpClientConfiguration.class);
addEnvironment(parent, "foo.ribbon.MaxAutoRetries:2");
factory.setApplicationContext(parent);
DefaultLoadBalancerRetryHandler retryHandler = (DefaultLoadBalancerRetryHandler) factory

View File

@@ -25,6 +25,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.feign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer;
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;
@@ -45,7 +46,7 @@ import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class})
FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class})
public class SpringRetryEnabledTests implements ApplicationContextAware {
private ApplicationContext context;

View File

@@ -24,12 +24,14 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
@@ -169,7 +171,7 @@ public class RibbonLoadBalancingHttpClientTests {
@Test
public void testNeverRetry() throws Exception {
ServerIntrospector introspector = mock(ServerIntrospector.class);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
HttpResponse response = mock(HttpResponse.class);
doThrow(new IOException("boom")).when(delegate).execute(any(HttpUriRequest.class));
DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
@@ -220,8 +222,8 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.GET;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
final HttpResponse response = mock(HttpResponse.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
@@ -252,8 +254,8 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.GET;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
final HttpResponse response = mock(HttpResponse.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
@@ -285,7 +287,7 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.POST;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
@@ -318,7 +320,7 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.POST;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
when(delegate).execute(any(HttpUriRequest.class));
@@ -353,8 +355,8 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.GET;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
final HttpResponse response = mock(HttpResponse.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
@@ -436,19 +438,19 @@ public class RibbonLoadBalancingHttpClientTests {
IClientConfig configOverride, SpringClientFactory factory)
throws Exception {
factory.setApplicationContext(new AnnotationConfigApplicationContext(
factory.setApplicationContext(new AnnotationConfigApplicationContext(HttpClientConfiguration.class,
RibbonAutoConfiguration.class, defaultConfigurationClass));
String serviceName = "foo";
String host = serviceName;
int port = 80;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
RibbonLoadBalancingHttpClient client = factory.getClient("service",
RibbonLoadBalancingHttpClient.class);
ReflectionTestUtils.setField(client, "delegate", delegate);
ReflectionTestUtils.setField(client, "lb", loadBalancer);
HttpResponse httpResponse = mock(HttpResponse.class);
CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(httpResponse).getStatusLine();

View File

@@ -20,6 +20,8 @@ import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -124,7 +126,7 @@ public class OkHttpLoadBalancingClientTests {
IClientConfig configOverride,
SpringClientFactory factory) throws Exception {
factory.setApplicationContext(new AnnotationConfigApplicationContext(
RibbonAutoConfiguration.class, defaultConfigurationClass));
RibbonAutoConfiguration.class, OkHttpClientConfiguration.class, defaultConfigurationClass));
OkHttpLoadBalancingClient client = factory.getClient("service",
OkHttpLoadBalancingClient.class);
@@ -132,6 +134,19 @@ public class OkHttpLoadBalancingClientTests {
return client.getOkHttpClient(configOverride, false);
}
@Configuration
protected static class OkHttpClientConfiguration {
@Autowired(required = false)
IClientConfig clientConfig;
@Bean
public OkHttpLoadBalancingClient okHttpLoadBalancingClient() {
if(clientConfig == null) {
clientConfig = new DefaultClientConfigImpl();
}
return new OkHttpLoadBalancingClient(new OkHttpClient(), clientConfig, new DefaultServerIntrospector());
}
}
@Configuration
protected static class UseDefaults {

View File

@@ -25,6 +25,7 @@ import org.springframework.cloud.ClassPathExclusions;
import org.springframework.cloud.FilteredClassPathRunner;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
@@ -37,32 +38,42 @@ import static org.hamcrest.Matchers.instanceOf;
* @author Ryan Baxter
*/
@RunWith(FilteredClassPathRunner.class)
@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"})
@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" })
public class SpringRetryDisableOkHttpClientTests {
private ConfigurableApplicationContext context;
@Before
public void setUp() {
context = new SpringApplicationBuilder().web(false).properties("ribbon.okhttp.enabled=true")
.sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class).run();
context = new SpringApplicationBuilder().web(false)
.properties("ribbon.okhttp.enabled=true")
.sources(RibbonAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
HttpClientConfiguration.class,
OkHttpLoadBalancingClientTests.OkHttpClientConfiguration.class,
RibbonClientConfiguration.class)
.run();
}
@After
public void tearDown() {
if(context != null) {
if (context != null) {
context.close();
}
}
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
Map<String, LoadBalancedRetryPolicyFactory> factories = context
.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
assertThat(factories.values(), hasSize(1));
assertThat(factories.values().toArray()[0], instanceOf(LoadBalancedRetryPolicyFactory.NeverRetryFactory.class));
Map<String, OkHttpLoadBalancingClient> clients = context.getBeansOfType(OkHttpLoadBalancingClient.class);
assertThat(factories.values().toArray()[0],
instanceOf(LoadBalancedRetryPolicyFactory.NeverRetryFactory.class));
Map<String, OkHttpLoadBalancingClient> clients = context
.getBeansOfType(OkHttpLoadBalancingClient.class);
assertThat(clients.values(), hasSize(1));
assertThat(clients.values().toArray()[0], instanceOf(OkHttpLoadBalancingClient.class));
assertThat(clients.values().toArray()[0],
instanceOf(OkHttpLoadBalancingClient.class));
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
@@ -38,20 +39,26 @@ import static org.hamcrest.Matchers.instanceOf;
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(value = {"ribbon.okhttp.enabled: true"})
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class})
@SpringBootTest(value = { "ribbon.okhttp.enabled: true", "ribbon.httpclient.enabled: false" })
@ContextConfiguration(classes = { RibbonAutoConfiguration.class,
HttpClientConfiguration.class, RibbonClientConfiguration.class,
LoadBalancerAutoConfiguration.class })
public class SpringRetryEnabledOkHttpClientTests implements ApplicationContextAware {
private ApplicationContext context;
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
Map<String, LoadBalancedRetryPolicyFactory> factories = context
.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
assertThat(factories.values(), hasSize(1));
assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
Map<String, OkHttpLoadBalancingClient> clients = context.getBeansOfType(OkHttpLoadBalancingClient.class);
assertThat(factories.values().toArray()[0],
instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
Map<String, OkHttpLoadBalancingClient> clients = context
.getBeansOfType(OkHttpLoadBalancingClient.class);
assertThat(clients.values(), hasSize(1));
assertThat(clients.values().toArray()[0], instanceOf(RetryableOkHttpLoadBalancingClient.class));
assertThat(clients.values().toArray()[0],
instanceOf(RetryableOkHttpLoadBalancingClient.class));
}
@Override

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.netflix.ribbon.test;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
@@ -40,7 +41,7 @@ import com.netflix.loadbalancer.ServerListSubsetFilter;
*/
@Configuration
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
UtilAutoConfiguration.class, RibbonAutoConfiguration.class })
UtilAutoConfiguration.class, RibbonAutoConfiguration.class, HttpClientConfiguration.class })
// tag::sample_default_ribbon_config[]
@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {

View File

@@ -43,19 +43,16 @@ public class ZuulProxyConfigurationTests {
@Test
public void testDefaultsToApacheHttpClient() {
testClient(HttpClientRibbonCommandFactory.class, null);
testClient(HttpClientRibbonCommandFactory.class, "zuul.ribbon.httpclient.enabled=true");
testClient(HttpClientRibbonCommandFactory.class, "ribbon.httpclient.enabled=true");
}
@Test
public void testEnableRestClient() {
testClient(RestClientRibbonCommandFactory.class, "zuul.ribbon.restclient.enabled=true");
testClient(RestClientRibbonCommandFactory.class, "ribbon.restclient.enabled=true");
}
@Test
public void testEnableOkHttpClient() {
testClient(OkHttpRibbonCommandFactory.class, "zuul.ribbon.okhttp.enabled=true");
testClient(OkHttpRibbonCommandFactory.class, "ribbon.okhttp.enabled=true");
}

View File

@@ -23,9 +23,12 @@ import com.netflix.zuul.context.RequestContext;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -34,10 +37,15 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
@@ -208,18 +216,33 @@ class SampleCustomZuulProxyApplication {
@Configuration
@EnableZuulProxy
@AutoConfigureBefore({FeignRibbonClientAutoConfiguration.class})
protected static class CustomZuulProxyConfig {
@Bean
public ApacheHttpClientFactory customHttpClientFactory() {
return new CustomApacheHttpClientFactory();
}
@Bean
public CloseableHttpClient closeableClient() {
return HttpClients.custom()
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultRequestConfig(RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT).build())
.build();
}
@Bean
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties) {
return new CustomHostRoutingFilter(helper, zuulProperties);
ZuulProperties zuulProperties, CloseableHttpClient httpClient) {
return new CustomHostRoutingFilter(helper, zuulProperties, httpClient);
}
private class CustomHostRoutingFilter extends SimpleHostRoutingFilter {
public CustomHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties) {
super(helper, zuulProperties);
ZuulProperties zuulProperties, CloseableHttpClient httpClient) {
super(helper, zuulProperties, httpClient);
}
@Override
@@ -227,18 +250,10 @@ class SampleCustomZuulProxyApplication {
super.addIgnoredHeaders("X-Ignored");
return super.run();
}
}
@Override
protected CloseableHttpClient newClient() {
// Custom client with cookie support.
// In practice, we would want a custom cookie store using a multimap with
// a user key.
return HttpClients.custom().setConnectionManager(newConnectionManager())
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultRequestConfig(RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT).build())
.build();
}
private class CustomApacheHttpClientFactory extends DefaultApacheHttpClientFactory {
}
}

View File

@@ -47,6 +47,10 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
@@ -113,7 +117,7 @@ public class SimpleHostRoutingFilterTests {
"zuul.host.maxPerRouteConnections=10", "zuul.host.timeToLive=5",
"zuul.host.timeUnit=SECONDS");
setupContext();
PoolingHttpClientConnectionManager connMgr = getFilter().newConnectionManager();
PoolingHttpClientConnectionManager connMgr = (PoolingHttpClientConnectionManager)getFilter().getConnectionManager();
assertEquals(100, connMgr.getMaxTotal());
assertEquals(10, connMgr.getDefaultMaxPerRoute());
Object pool = getField(connMgr, "pool");
@@ -148,7 +152,7 @@ public class SimpleHostRoutingFilterTests {
@Test
public void defaultPropertiesAreApplied() {
setupContext();
PoolingHttpClientConnectionManager connMgr = getFilter().newConnectionManager();
PoolingHttpClientConnectionManager connMgr = (PoolingHttpClientConnectionManager)getFilter().getConnectionManager();
assertEquals(200, connMgr.getMaxTotal());
assertEquals(20, connMgr.getDefaultMaxPerRoute());
@@ -222,8 +226,16 @@ public class SimpleHostRoutingFilterTests {
}
@Bean
SimpleHostRoutingFilter simpleHostRoutingFilter(ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(new ProxyRequestHelper(), zuulProperties);
ApacheHttpClientFactory clientFactory() {return new DefaultApacheHttpClientFactory(); }
@Bean
ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { return new DefaultApacheHttpClientConnectionManagerFactory(); }
@Bean
SimpleHostRoutingFilter simpleHostRoutingFilter(ZuulProperties zuulProperties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory clientFactory) {
return new SimpleHostRoutingFilter(new ProxyRequestHelper(), zuulProperties, connectionManagerFactory, clientFactory);
}
}
}

View File

@@ -32,9 +32,12 @@ import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
import org.springframework.cloud.netflix.ribbon.test.TestLoadBalancer;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
@@ -49,6 +52,9 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = OkHttpRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
@@ -119,5 +125,19 @@ public class OkHttpRibbonCommandIntegrationTests extends ZuulProxyTestBase {
public MyErrorController myErrorController(ErrorAttributes errorAttributes) {
return new MyErrorController(errorAttributes);
}
@Bean
public IClientConfig config() {
return new DefaultClientConfigImpl();
}
@Bean
public OkHttpLoadBalancingClient okClient(IClientConfig config) {
final OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config,
new DefaultServerIntrospector());
client.setLoadBalancer(new TestLoadBalancer<>());
client.setRetryHandler(new DefaultLoadBalancerRetryHandler());
return client;
}
}
}