Commit 981fa5db by Ryan Baxter

Merging PR #1689 into master

parent bcb97bce
...@@ -1479,6 +1479,20 @@ The default HTTP client used by zuul is now backed by the Apache HTTP Client ins ...@@ -1479,6 +1479,20 @@ The default HTTP client used by zuul is now backed by the Apache HTTP Client ins
deprecated Ribbon `RestClient`. To use `RestClient` or to use the `okhttp3.OkHttpClient` set 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.
==== Retrying Failed Requests
When using the Apache Http Client or the OK HTTP Client you can enable them to automatically
retry failed requests by adding https://github.com/spring-projects/spring-retry[Spring Retry]
to your application's classpath. When Zuul uses Ribbon, Zuul will honor some of the Ribbon
configuration values related to retrying failed requests. The properties you can use are
`client.ribbon.MaxAutoRetries`, `client.ribbon.MaxAutoRetriesNextServer`, and
`client.ribbon.OkToRetryOnAllOperations`. See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties[Ribbon documentation]
for a description of what there properties do.
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`.
=== Cookies and Sensitive Headers === Cookies and Sensitive Headers
It's OK to share headers between services in the same system, but you It's OK to share headers between services in the same system, but you
......
...@@ -28,9 +28,9 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -28,9 +28,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration; import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
...@@ -38,11 +38,9 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFact ...@@ -38,11 +38,9 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFact
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer; import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.AsyncRestTemplate; import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
...@@ -91,6 +89,12 @@ public class RibbonAutoConfiguration { ...@@ -91,6 +89,12 @@ public class RibbonAutoConfiguration {
} }
@Bean @Bean
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public LoadBalancedRetryPolicyFactory neverRetryPolicyFactory() {
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
}
@Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() { public PropertiesFactory propertiesFactory() {
return new PropertiesFactory(); return new PropertiesFactory();
......
...@@ -26,11 +26,14 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -26,11 +26,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient; import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient; import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
import org.springframework.cloud.netflix.ribbon.okhttp.RetryableOkHttpLoadBalancingClient;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
...@@ -125,11 +128,26 @@ public class RibbonClientConfiguration { ...@@ -125,11 +128,26 @@ public class RibbonClientConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class) @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient( public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector, IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler) {
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(
config, serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler, ILoadBalancer loadBalancer, RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) { LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient( RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
config, serverIntrospector, loadBalancedRetryPolicyFactory); config, serverIntrospector, loadBalancedRetryPolicyFactory);
client.setLoadBalancer(loadBalancer); client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler); client.setRetryHandler(retryHandler);
...@@ -145,18 +163,37 @@ public class RibbonClientConfiguration { ...@@ -145,18 +163,37 @@ public class RibbonClientConfiguration {
@Value("${ribbon.client.name}") @Value("${ribbon.client.name}")
private String name = "client"; private String name = "client";
@Bean @Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class) @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
public OkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config, @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer, public RetryableOkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config,
RetryHandler retryHandler, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) { ServerIntrospector serverIntrospector,
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config, ILoadBalancer loadBalancer,
RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(config,
serverIntrospector, loadBalancedRetryPolicyFactory); serverIntrospector, loadBalancedRetryPolicyFactory);
client.setLoadBalancer(loadBalancer); client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler); client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client); Monitors.registerObject("Client_" + this.name, client);
return client; return client;
} }
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public OkHttpLoadBalancingClient retryableOkHttpLoadBalancingClient(IClientConfig config,
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
RetryHandler retryHandler) {
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config,
serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
} }
@Configuration @Configuration
......
/*
* Copyright 2013-2017 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.ribbon.apache;
import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang.BooleanUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.http.HttpRequest;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.util.UriComponentsBuilder;
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.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) {
super(config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
@Override
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));
final RequestConfig requestConfig = builder.build();
return this.executeWithRetry(request, 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
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(),
newRequest.getURI().getFragment()));
}
}
if (isSecure(configOverride)) {
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);
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
}
});
}
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
RetryTemplate retryTemplate = new RetryTemplate();
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);
}
@Override
public ServiceInstance choose(String serviceId) {
Server server = this.getLoadBalancer().chooseServer(serviceId);
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
server);
}
@Override
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) {
super(request, policy, serviceInstanceChooser, serviceName);
}
}
}
...@@ -27,14 +27,8 @@ import org.apache.http.client.HttpClient; ...@@ -27,14 +27,8 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient; import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
import org.springframework.cloud.netflix.ribbon.support.RetryableLoadBalancingClient;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI; import java.net.URI;
...@@ -46,8 +40,8 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps ...@@ -46,8 +40,8 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
* @author Ryan Baxter * @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 public class RibbonLoadBalancingHttpClient extends
extends RetryableLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> { AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> {
@Deprecated @Deprecated
public RibbonLoadBalancingHttpClient() { public RibbonLoadBalancingHttpClient() {
...@@ -67,11 +61,6 @@ public class RibbonLoadBalancingHttpClient ...@@ -67,11 +61,6 @@ public class RibbonLoadBalancingHttpClient
super(delegate, config, serverIntrospector); super(delegate, config, serverIntrospector);
} }
public RibbonLoadBalancingHttpClient(IClientConfig iClientConfig, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(iClientConfig, serverIntrospector, loadBalancedRetryPolicyFactory);
}
protected HttpClient createDelegate(IClientConfig config) { protected HttpClient createDelegate(IClientConfig config) {
return HttpClientBuilder.create() return HttpClientBuilder.create()
// already defaults to 0 in builder, so resetting to 0 won't hurt // already defaults to 0 in builder, so resetting to 0 won't hurt
...@@ -84,7 +73,7 @@ public class RibbonLoadBalancingHttpClient ...@@ -84,7 +73,7 @@ public class RibbonLoadBalancingHttpClient
} }
@Override @Override
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,
final IClientConfig configOverride) throws Exception { final IClientConfig configOverride) throws Exception {
final RequestConfig.Builder builder = RequestConfig.custom(); final RequestConfig.Builder builder = RequestConfig.custom();
IClientConfig config = configOverride != null ? configOverride : this.config; IClientConfig config = configOverride != null ? configOverride : this.config;
...@@ -96,32 +85,14 @@ public class RibbonLoadBalancingHttpClient ...@@ -96,32 +85,14 @@ public class RibbonLoadBalancingHttpClient
CommonClientConfigKey.FollowRedirects, this.followRedirects)); CommonClientConfigKey.FollowRedirects, this.followRedirects));
final RequestConfig requestConfig = builder.build(); final RequestConfig requestConfig = builder.build();
return this.executeWithRetry(request, new RetryCallback() { if (isSecure(configOverride)) {
@Override final URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception { .scheme("https").build().toUri();
//on retries the policy will choose the server and set it in the context request = request.withNewUri(secureUri);
//extract the server and update the request being made }
RibbonApacheHttpRequest newRequest = request; final HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
if(context instanceof LoadBalancedRetryContext) { final HttpResponse httpResponse = this.delegate.execute(httpUriRequest);
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance(); return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
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();
newRequest = newRequest.withNewUri(secureUri);
}
HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
final HttpResponse httpResponse = RibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
}
});
} }
@Override @Override
...@@ -129,4 +100,9 @@ public class RibbonLoadBalancingHttpClient ...@@ -129,4 +100,9 @@ public class RibbonLoadBalancingHttpClient
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server); URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri); return super.reconstructURIWithServer(server, uri);
} }
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
}
} }
...@@ -19,13 +19,8 @@ package org.springframework.cloud.netflix.ribbon.okhttp; ...@@ -19,13 +19,8 @@ package org.springframework.cloud.netflix.ribbon.okhttp;
import java.net.URI; import java.net.URI;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.support.RetryableLoadBalancingClient; import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.CommonClientConfigKey;
...@@ -44,7 +39,7 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps ...@@ -44,7 +39,7 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
* @author Ryan Baxter * @author Ryan Baxter
*/ */
public class OkHttpLoadBalancingClient public class OkHttpLoadBalancingClient
extends RetryableLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> { extends AbstractLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {
@Deprecated @Deprecated
public OkHttpLoadBalancingClient() { public OkHttpLoadBalancingClient() {
...@@ -61,12 +56,6 @@ public class OkHttpLoadBalancingClient ...@@ -61,12 +56,6 @@ public class OkHttpLoadBalancingClient
super(config, serverIntrospector); super(config, serverIntrospector);
} }
public OkHttpLoadBalancingClient(IClientConfig config,
ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(config, serverIntrospector, loadBalancedRetryPolicyFactory);
}
public OkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config, public OkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config,
ServerIntrospector serverIntrospector) { ServerIntrospector serverIntrospector) {
super(delegate, config, serverIntrospector); super(delegate, config, serverIntrospector);
...@@ -78,36 +67,19 @@ public class OkHttpLoadBalancingClient ...@@ -78,36 +67,19 @@ public class OkHttpLoadBalancingClient
} }
@Override @Override
public OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest, public OkHttpRibbonResponse execute(OkHttpRibbonRequest ribbonRequest,
final IClientConfig configOverride) throws Exception { final IClientConfig configOverride) throws Exception {
return this.executeWithRetry(ribbonRequest, new RetryCallback() { boolean secure = isSecure(configOverride);
@Override if (secure) {
public OkHttpRibbonResponse doWithRetry(RetryContext context) throws Exception { final URI secureUri = UriComponentsBuilder.fromUri(ribbonRequest.getUri())
//on retries the policy will choose the server and set it in the context .scheme("https").build().toUri();
//extract the server and update the request being made ribbonRequest = ribbonRequest.withNewUri(secureUri);
OkHttpRibbonRequest newRequest = ribbonRequest; }
if(context instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance(); OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
if(service != null) { final Request request = ribbonRequest.toRequest();
//Reconstruct the request URI using the host and port set in the retry context Response response = httpClient.newCall(request).execute();
newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(), return new OkHttpRibbonResponse(response, ribbonRequest.getUri());
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();
newRequest = newRequest.withNewUri(secureUri);
}
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
final Request request = newRequest.toRequest();
Response response = httpClient.newCall(request).execute();
return new OkHttpRibbonResponse(response, newRequest.getUri());
}
});
} }
OkHttpClient getOkHttpClient(IClientConfig configOverride, boolean secure) { OkHttpClient getOkHttpClient(IClientConfig configOverride, boolean secure) {
......
/* /*
* * Copyright 2013-2017 the original author or authors.
* Copyright 2013-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,15 +12,18 @@ ...@@ -13,15 +12,18 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package org.springframework.cloud.netflix.ribbon.okhttp;
package org.springframework.cloud.netflix.ribbon.support; import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.BooleanUtils;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser; import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
...@@ -30,57 +32,32 @@ import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; ...@@ -30,57 +32,32 @@ import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.policy.NeverRetryPolicy; import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate; import org.springframework.retry.support.RetryTemplate;
import com.netflix.client.IResponse; import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.RequestSpecificRetryHandler; import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler; import com.netflix.client.RetryHandler;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
/** /**
* A load balancing client which uses Spring Retry to retry failed requests. * An OK HTTP client which leverages Spring Retry to retry failed request.
* @author Ryan Baxter * @author Ryan Baxter
*/ */
public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest, T extends IResponse, D> public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClient implements ServiceInstanceChooser {
extends AbstractLoadBalancingClient<S, T, D> implements ServiceInstanceChooser {
protected LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory =
new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
@Deprecated
public RetryableLoadBalancingClient() {
super();
}
@Deprecated private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
public RetryableLoadBalancingClient(final ILoadBalancer lb) {
super(lb);
}
public RetryableLoadBalancingClient(IClientConfig config, ServerIntrospector serverIntrospector) { public RetryableOkHttpLoadBalancingClient(IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(config, serverIntrospector); super(config, serverIntrospector);
}
public RetryableLoadBalancingClient(D delegate, IClientConfig config, ServerIntrospector serverIntrospector) {
super(delegate, config, serverIntrospector);
}
public RetryableLoadBalancingClient(IClientConfig iClientConfig, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
this(iClientConfig, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory; this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
} }
/** private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request,
* Executes a {@link S} using Spring Retry. RetryCallback<OkHttpRibbonResponse, IOException> callback)
* @param request The request to execute. throws Exception {
* @param callback The retry callback to use.
* @return The response.
* @throws Exception Thrown if there is an error making the request and a retry cannot be completed successfully.
*/
protected T executeWithRetry(S request, RetryCallback<T, IOException> callback) throws Exception {
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this); LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
RetryTemplate retryTemplate = new RetryTemplate(); RetryTemplate retryTemplate = new RetryTemplate();
boolean retryable = request.getContext() == null ? true : boolean retryable = request.getContext() == null ? true :
...@@ -91,6 +68,39 @@ public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest ...@@ -91,6 +68,39 @@ public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest
} }
@Override @Override
public OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest,
final IClientConfig configOverride) throws Exception {
return this.executeWithRetry(ribbonRequest, new RetryCallback() {
@Override
public OkHttpRibbonResponse 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
OkHttpRibbonRequest newRequest = ribbonRequest;
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();
newRequest = newRequest.withNewUri(secureUri);
}
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
final Request request = newRequest.toRequest();
Response response = httpClient.newCall(request).execute();
return new OkHttpRibbonResponse(response, newRequest.getUri());
}
});
}
@Override
public ServiceInstance choose(String serviceId) { public ServiceInstance choose(String serviceId) {
Server server = this.getLoadBalancer().chooseServer(serviceId); Server server = this.getLoadBalancer().chooseServer(serviceId);
return new RibbonLoadBalancerClient.RibbonServer(serviceId, return new RibbonLoadBalancerClient.RibbonServer(serviceId,
...@@ -98,7 +108,7 @@ public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest ...@@ -98,7 +108,7 @@ public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest
} }
@Override @Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(S request, IClientConfig requestConfig) { public RequestSpecificRetryHandler getRequestSpecificRetryHandler(OkHttpRibbonRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null); return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
} }
...@@ -107,5 +117,4 @@ public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest ...@@ -107,5 +117,4 @@ public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest
super(request, policy, serviceInstanceChooser, serviceName); super(request, policy, serviceInstanceChooser, serviceName);
} }
} }
} }
...@@ -28,10 +28,12 @@ import org.springframework.cloud.ClassPathExclusions; ...@@ -28,10 +28,12 @@ import org.springframework.cloud.ClassPathExclusions;
import org.springframework.cloud.FilteredClassPathRunner; import org.springframework.cloud.FilteredClassPathRunner;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
/** /**
* @author Ryan Baxter * @author Ryan Baxter
...@@ -45,7 +47,7 @@ public class SpringRetryDisabledTests { ...@@ -45,7 +47,7 @@ public class SpringRetryDisabledTests {
@Before @Before
public void setUp() { public void setUp() {
context = new SpringApplicationBuilder().web(false) context = new SpringApplicationBuilder().web(false)
.sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class).run(); .sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class).run();
} }
@After @After
...@@ -58,6 +60,10 @@ public class SpringRetryDisabledTests { ...@@ -58,6 +60,10 @@ public class SpringRetryDisabledTests {
@Test @Test
public void testLoadBalancedRetryFactoryBean() throws Exception { 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(0)); assertThat(factories.values(), hasSize(1));
assertThat(factories.values().toArray()[0], instanceOf(LoadBalancedRetryPolicyFactory.NeverRetryFactory.class));
Map<String, RibbonLoadBalancingHttpClient> clients = context.getBeansOfType(RibbonLoadBalancingHttpClient.class);
assertThat(clients.values(), hasSize(1));
assertThat(clients.values().toArray()[0], instanceOf(RibbonLoadBalancingHttpClient.class));
} }
} }
...@@ -24,6 +24,8 @@ import org.junit.runner.RunWith; ...@@ -24,6 +24,8 @@ import org.junit.runner.RunWith;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
...@@ -37,7 +39,7 @@ import static org.hamcrest.collection.IsCollectionWithSize.hasSize; ...@@ -37,7 +39,7 @@ import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
* @author Ryan Baxter * @author Ryan Baxter
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class}) @ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class})
public class SpringRetryEnabledTests implements ApplicationContextAware { public class SpringRetryEnabledTests implements ApplicationContextAware {
private ApplicationContext context; private ApplicationContext context;
...@@ -47,6 +49,9 @@ public class SpringRetryEnabledTests implements ApplicationContextAware { ...@@ -47,6 +49,9 @@ public class SpringRetryEnabledTests implements ApplicationContextAware {
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class); Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
assertThat(factories.values(), hasSize(1)); assertThat(factories.values(), hasSize(1));
assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class)); assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
Map<String, RibbonLoadBalancingHttpClient> clients = context.getBeansOfType(RibbonLoadBalancingHttpClient.class);
assertThat(clients.values(), hasSize(1));
assertThat(clients.values().toArray()[0], instanceOf(RetryableRibbonLoadBalancingHttpClient.class));
} }
@Override @Override
......
...@@ -113,8 +113,8 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -113,8 +113,8 @@ public class RibbonLoadBalancingHttpClientTests {
SpringClientFactory factory = new SpringClientFactory(); SpringClientFactory factory = new SpringClientFactory();
factory.setApplicationContext(new AnnotationConfigApplicationContext( factory.setApplicationContext(new AnnotationConfigApplicationContext(
RibbonAutoConfiguration.class, Connections.class)); RibbonAutoConfiguration.class, Connections.class));
RibbonLoadBalancingHttpClient client = factory.getClient("service", RetryableRibbonLoadBalancingHttpClient client = factory.getClient("service",
RibbonLoadBalancingHttpClient.class); RetryableRibbonLoadBalancingHttpClient.class);
HttpClient delegate = client.getDelegate(); HttpClient delegate = client.getDelegate();
PoolingHttpClientConnectionManager connManager = (PoolingHttpClientConnectionManager) ReflectionTestUtils.getField(delegate, "connManager"); PoolingHttpClientConnectionManager connManager = (PoolingHttpClientConnectionManager) ReflectionTestUtils.getField(delegate, "connManager");
...@@ -182,7 +182,7 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -182,7 +182,7 @@ public class RibbonLoadBalancingHttpClientTests {
} }
} }
private RibbonLoadBalancingHttpClient setupClientForRetry(int retriesNextServer, int retriesSameServer, private RetryableRibbonLoadBalancingHttpClient setupClientForRetry(int retriesNextServer, int retriesSameServer,
boolean retryable, boolean retryOnAllOps, boolean retryable, boolean retryOnAllOps,
String serviceName, String host, int port, String serviceName, String host, int port,
HttpClient delegate, ILoadBalancer lb) throws Exception { HttpClient delegate, ILoadBalancer lb) throws Exception {
...@@ -198,7 +198,7 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -198,7 +198,7 @@ public class RibbonLoadBalancingHttpClientTests {
SpringClientFactory clientFactory = mock(SpringClientFactory.class); SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(context).when(clientFactory).getLoadBalancerContext(eq(serviceName)); doReturn(context).when(clientFactory).getLoadBalancerContext(eq(serviceName));
LoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory); LoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(clientConfig, introspector, factory); RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(clientConfig, introspector, factory);
client.setLoadBalancer(lb); client.setLoadBalancer(lb);
ReflectionTestUtils.setField(client, "delegate", delegate); ReflectionTestUtils.setField(client, "delegate", delegate);
return client; return client;
...@@ -219,7 +219,7 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -219,7 +219,7 @@ public class RibbonLoadBalancingHttpClientTests {
final HttpResponse response = mock(HttpResponse.class); final HttpResponse response = mock(HttpResponse.class);
doThrow(new IOException("boom")).doReturn(response).when(delegate).execute(any(HttpUriRequest.class)); doThrow(new IOException("boom")).doReturn(response).when(delegate).execute(any(HttpUriRequest.class));
ILoadBalancer lb = mock(ILoadBalancer.class); ILoadBalancer lb = mock(ILoadBalancer.class);
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps, RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
serviceName, host, port, delegate, lb); serviceName, host, port, delegate, lb);
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class); RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
doReturn(uri).when(request).getURI(); doReturn(uri).when(request).getURI();
...@@ -249,7 +249,7 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -249,7 +249,7 @@ public class RibbonLoadBalancingHttpClientTests {
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response). doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
when(delegate).execute(any(HttpUriRequest.class)); when(delegate).execute(any(HttpUriRequest.class));
ILoadBalancer lb = mock(ILoadBalancer.class); ILoadBalancer lb = mock(ILoadBalancer.class);
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps, RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
serviceName, host, port, delegate, lb); serviceName, host, port, delegate, lb);
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class); RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
doReturn(uri).when(request).getURI(); doReturn(uri).when(request).getURI();
...@@ -279,7 +279,7 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -279,7 +279,7 @@ public class RibbonLoadBalancingHttpClientTests {
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response). doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
when(delegate).execute(any(HttpUriRequest.class)); when(delegate).execute(any(HttpUriRequest.class));
ILoadBalancer lb = mock(ILoadBalancer.class); ILoadBalancer lb = mock(ILoadBalancer.class);
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps, RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
serviceName, host, port, delegate, lb); serviceName, host, port, delegate, lb);
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class); RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
doReturn(method).when(request).getMethod(); doReturn(method).when(request).getMethod();
...@@ -308,7 +308,7 @@ public class RibbonLoadBalancingHttpClientTests { ...@@ -308,7 +308,7 @@ public class RibbonLoadBalancingHttpClientTests {
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response). doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
when(delegate).execute(any(HttpUriRequest.class)); when(delegate).execute(any(HttpUriRequest.class));
ILoadBalancer lb = mock(ILoadBalancer.class); ILoadBalancer lb = mock(ILoadBalancer.class);
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps, RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
serviceName, host, port, delegate, lb); serviceName, host, port, delegate, lb);
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class); RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
doReturn(method).when(request).getMethod(); doReturn(method).when(request).getMethod();
......
/*
* Copyright 2013-2017 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.ribbon.okhttp;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.builder.SpringApplicationBuilder;
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.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
/**
* @author Ryan Baxter
*/
@RunWith(FilteredClassPathRunner.class)
@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();
}
@After
public void tearDown() {
if(context != null) {
context.close();
}
}
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
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(clients.values(), hasSize(1));
assertThat(clients.values().toArray()[0], instanceOf(OkHttpLoadBalancingClient.class));
}
}
/*
* Copyright 2013-2017 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.ribbon.okhttp;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
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.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
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})
public class SpringRetryEnabledOkHttpClientTests implements ApplicationContextAware {
private ApplicationContext context;
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
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(clients.values(), hasSize(1));
assertThat(clients.values().toArray()[0], instanceOf(RetryableOkHttpLoadBalancingClient.class));
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}
...@@ -21,14 +21,6 @@ ...@@ -21,14 +21,6 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId> <artifactId>spring-cloud-starter</artifactId>
</dependency> </dependency>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment