Merge pull request #1836 from ryanjbaxter/response-code-retry
Add the ability for ribbon clients to specify response status codes they would like to retry
This commit is contained in:
@@ -2461,6 +2461,22 @@ certain Ribbon properties. The properties you can use are
|
||||
`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.
|
||||
|
||||
In addition you may want to retry requests when certain status codes are returned in the
|
||||
response. You can list the response codes you would like the Ribbon client to retry using the
|
||||
property `clientName.ribbon.retryableStatusCodes`. For example
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
clientName:
|
||||
ribbon:
|
||||
retryableStatusCodes: 404,502
|
||||
----
|
||||
|
||||
You can also create a bean of type `LoadBalancedRetryPolicy` and implement the `retryableStatusCode`
|
||||
method to determine whether you want to retry a request given the status code.
|
||||
|
||||
|
||||
|
||||
==== Zuul
|
||||
|
||||
You can turn off Zuul's retry functionality by setting `zuul.retryable` to `false`. You
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFact
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RetryableStatusCodeException;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
@@ -69,7 +70,7 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
|
||||
else {
|
||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||
}
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
|
||||
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName()));
|
||||
@@ -89,6 +90,9 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
|
||||
feignRequest = request.toRequest();
|
||||
}
|
||||
Response response = request.client().execute(feignRequest, options);
|
||||
if(retryPolicy.retryableStatusCode(response.status())) {
|
||||
throw new RetryableStatusCodeException(RetryableFeignLoadBalancer.this.getClientName(), response.status());
|
||||
}
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -90,12 +90,14 @@ public class RibbonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
@ConditionalOnMissingBean
|
||||
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
|
||||
return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
|
||||
@ConditionalOnMissingBean
|
||||
public LoadBalancedRetryPolicyFactory neverRetryPolicyFactory() {
|
||||
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
*
|
||||
* * 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.ribbon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.client.config.IClientConfigKey;
|
||||
|
||||
/**
|
||||
* {@link LoadBalancedRetryPolicy} for Ribbon clients.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonLoadBalancedRetryPolicy implements LoadBalancedRetryPolicy {
|
||||
|
||||
public static final IClientConfigKey<String> RETRYABLE_STATUS_CODES = new CommonClientConfigKey<String>("retryableStatusCodes") {};
|
||||
private int sameServerCount = 0;
|
||||
private int nextServerCount = 0;
|
||||
private String serviceId;
|
||||
private RibbonLoadBalancerContext lbContext;
|
||||
private ServiceInstanceChooser loadBalanceChooser;
|
||||
List<Integer> retryableStatusCodes = new ArrayList<>();
|
||||
|
||||
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser) {
|
||||
this.serviceId = serviceId;
|
||||
this.lbContext = context;
|
||||
this.loadBalanceChooser = loadBalanceChooser;
|
||||
}
|
||||
|
||||
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser,
|
||||
IClientConfig clientConfig) {
|
||||
this.serviceId = serviceId;
|
||||
this.lbContext = context;
|
||||
this.loadBalanceChooser = loadBalanceChooser;
|
||||
String retryableStatusCodesProp = clientConfig.getPropertyAsString(RETRYABLE_STATUS_CODES, "");
|
||||
String[] retryableStatusCodesArray = retryableStatusCodesProp.split(",");
|
||||
for(String code : retryableStatusCodesArray) {
|
||||
if(!StringUtils.isEmpty(code)) {
|
||||
try {
|
||||
retryableStatusCodes.add(Integer.valueOf(code));
|
||||
} catch (NumberFormatException e) {
|
||||
//TODO log
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canRetry(LoadBalancedRetryContext context) {
|
||||
HttpMethod method = context.getRequest().getMethod();
|
||||
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
|
||||
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
|
||||
//this will be called after a failure occurs and we increment the counter
|
||||
//so we check that the count is less than or equals to too make sure
|
||||
//we try the next server the right number of times
|
||||
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(LoadBalancedRetryContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
|
||||
//Check if we need to ask the load balancer for a new server.
|
||||
//Do this before we increment the counters because the first call to this method
|
||||
//is not a retry it is just an initial failure.
|
||||
if(!canRetrySameServer(context) && canRetryNextServer(context)) {
|
||||
context.setServiceInstance(loadBalanceChooser.choose(serviceId));
|
||||
}
|
||||
//This method is called regardless of whether we are retrying or making the first request.
|
||||
//Since we do not count the initial request in the retry count we don't reset the counter
|
||||
//until we actually equal the same server count limit. This will allow us to make the initial
|
||||
//request plus the right number of retries.
|
||||
if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
|
||||
//reset same server since we are moving to a new server
|
||||
sameServerCount = 0;
|
||||
nextServerCount++;
|
||||
if(!canRetryNextServer(context)) {
|
||||
context.setExhaustedOnly();
|
||||
}
|
||||
} else {
|
||||
sameServerCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retryableStatusCode(int statusCode) {
|
||||
return retryableStatusCodes.contains(statusCode);
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,9 @@
|
||||
*/
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
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.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -34,60 +31,10 @@ public class RibbonLoadBalancedRetryPolicyFactory implements LoadBalancedRetryPo
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancedRetryPolicy create(final String serviceId, final ServiceInstanceChooser loadBalanceChooser) {
|
||||
final RibbonLoadBalancerContext lbContext = this.clientFactory
|
||||
public LoadBalancedRetryPolicy create(String serviceId, ServiceInstanceChooser loadBalanceChooser) {
|
||||
RibbonLoadBalancerContext lbContext = this.clientFactory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
return new LoadBalancedRetryPolicy() {
|
||||
private int sameServerCount = 0;
|
||||
private int nextServerCount = 0;
|
||||
private ServiceInstance lastServiceInstance = null;
|
||||
public boolean canRetry(LoadBalancedRetryContext context) {
|
||||
HttpMethod method = context.getRequest().getMethod();
|
||||
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
|
||||
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
|
||||
//this will be called after a failure occurs and we increment the counter
|
||||
//so we check that the count is less than or equals to too make sure
|
||||
//we try the next server the right number of times
|
||||
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(LoadBalancedRetryContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
|
||||
//Check if we need to ask the load balancer for a new server.
|
||||
//Do this before we increment the counters because the first call to this method
|
||||
//is not a retry it is just an initial failure.
|
||||
if(!canRetrySameServer(context) && canRetryNextServer(context)) {
|
||||
context.setServiceInstance(loadBalanceChooser.choose(serviceId));
|
||||
}
|
||||
//This method is called regardless of whether we are retrying or making the first request.
|
||||
//Since we do not count the initial request in the retry count we don't reset the counter
|
||||
//until we actually equal the same server count limit. This will allow us to make the initial
|
||||
//request plus the right number of retries.
|
||||
if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
|
||||
//reset same server since we are moving to a new server
|
||||
sameServerCount = 0;
|
||||
nextServerCount++;
|
||||
if(!canRetryNextServer(context)) {
|
||||
context.setExhaustedOnly();
|
||||
}
|
||||
} else {
|
||||
sameServerCount++;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
return new RibbonLoadBalancedRetryPolicy(serviceId, lbContext, loadBalanceChooser, clientFactory.getClientConfig(serviceId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ 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.cloud.netflix.ribbon.support.RetryableStatusCodeException;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
@@ -65,7 +66,8 @@ public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingH
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
|
||||
final RequestConfig requestConfig = builder.build();
|
||||
return this.executeWithRetry(request, new RetryCallback() {
|
||||
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
|
||||
@@ -88,13 +90,17 @@ public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingH
|
||||
}
|
||||
HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
|
||||
final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
|
||||
if(retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
|
||||
throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName,
|
||||
httpResponse.getStatusLine().getStatusCode());
|
||||
}
|
||||
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
|
||||
}
|
||||
});
|
||||
};
|
||||
return this.executeWithRetry(request, retryPolicy, retryCallback);
|
||||
}
|
||||
|
||||
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
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);
|
||||
|
||||
@@ -19,7 +19,6 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
@@ -30,6 +29,7 @@ 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.cloud.netflix.ribbon.support.RetryableStatusCodeException;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
@@ -55,10 +55,9 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
}
|
||||
|
||||
private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request,
|
||||
RetryCallback<OkHttpRibbonResponse, IOException> callback)
|
||||
private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request, LoadBalancedRetryPolicy retryPolicy,
|
||||
RetryCallback<OkHttpRibbonResponse, Exception> 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);
|
||||
@@ -70,7 +69,8 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
|
||||
@Override
|
||||
public OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
return this.executeWithRetry(ribbonRequest, new RetryCallback() {
|
||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryCallback<OkHttpRibbonResponse, Exception> retryCallback = new RetryCallback<OkHttpRibbonResponse, Exception>() {
|
||||
@Override
|
||||
public OkHttpRibbonResponse doWithRetry(RetryContext context) throws Exception {
|
||||
//on retries the policy will choose the server and set it in the context
|
||||
@@ -95,9 +95,13 @@ public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClien
|
||||
|
||||
final Request request = newRequest.toRequest();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
if(retryPolicy.retryableStatusCode(response.code())) {
|
||||
throw new RetryableStatusCodeException(RetryableOkHttpLoadBalancingClient.this.clientName, response.code());
|
||||
}
|
||||
return new OkHttpRibbonResponse(response, newRequest.getUri());
|
||||
}
|
||||
});
|
||||
};
|
||||
return this.executeWithRetry(ribbonRequest, retryPolicy, retryCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
*
|
||||
* * 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.ribbon.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Exception to be thrown when the status code is deemed to be retryable.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableStatusCodeException extends IOException {
|
||||
|
||||
private static final String MESSAGE = "Service %s returned a status code of %d";
|
||||
|
||||
public RetryableStatusCodeException(String serviceId, int statusCode) {
|
||||
super(String.format(MESSAGE, serviceId, statusCode));
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ 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.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
@@ -60,6 +62,7 @@ import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
@@ -98,6 +101,14 @@ public class RetryableFeignLoadBalancerTest {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
|
||||
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
|
||||
doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
@@ -139,6 +150,14 @@ public class RetryableFeignLoadBalancerTest {
|
||||
public void executeRetry() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
|
||||
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
@@ -154,6 +173,34 @@ public class RetryableFeignLoadBalancerTest {
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeRetryOnStatusCode() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
|
||||
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
|
||||
doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response fourOFourResponse = Response.builder().status(404).headers(new HashMap<String, Collection<String>>()).build();
|
||||
doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertEquals(200, ribbonResponse.toResponse().status());
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestSpecificRetryHandler() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
|
||||
@@ -86,6 +86,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
@@ -97,6 +98,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,6 +114,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
@@ -123,6 +126,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(false));
|
||||
assertThat(policy.canRetrySameServer(context), is(false));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,6 +142,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(server.getServiceId()).when(config).getClientName();
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).initWithNiwsConfig(config);
|
||||
@@ -149,6 +154,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
|
||||
assertThat(policy.canRetryNextServer(context), is(true));
|
||||
assertThat(policy.canRetrySameServer(context), is(true));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,6 +167,7 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(false).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
@@ -189,9 +196,32 @@ public class RibbonLoadBalancedRetryPolicyFactoryTest {
|
||||
}
|
||||
}
|
||||
assertThat(context.isExhaustedOnly(), is(true));
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
verify(context, times(4)).setServiceInstance(any(ServiceInstance.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryableStatusCodest() throws Exception {
|
||||
int sameServer = 3;
|
||||
int nextServer = 3;
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
|
||||
doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
|
||||
doReturn(false).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
|
||||
doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
doReturn(HttpMethod.GET).when(request).getMethod();
|
||||
assertThat(policy.retryableStatusCode(400), is(false));
|
||||
assertThat(policy.retryableStatusCode(404), is(true));
|
||||
assertThat(policy.retryableStatusCode(502), is(true));
|
||||
}
|
||||
|
||||
protected RibbonLoadBalancerClient getRibbonLoadBalancerClient(
|
||||
RibbonServer ribbonServer) {
|
||||
given(this.loadBalancer.getName()).willReturn(ribbonServer.getServiceId());
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.ribbon.apache;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
@@ -29,6 +30,7 @@ import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
@@ -185,7 +187,7 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
private RetryableRibbonLoadBalancingHttpClient setupClientForRetry(int retriesNextServer, int retriesSameServer,
|
||||
boolean retryable, boolean retryOnAllOps,
|
||||
String serviceName, String host, int port,
|
||||
HttpClient delegate, ILoadBalancer lb) throws Exception {
|
||||
HttpClient delegate, ILoadBalancer lb, String statusCodes) throws Exception {
|
||||
ServerIntrospector introspector = mock(ServerIntrospector.class);
|
||||
RetryHandler retryHandler = new DefaultLoadBalancerRetryHandler(retriesSameServer, retriesNextServer, retryable);
|
||||
doReturn(new Server(host, port)).when(lb).chooseServer(eq(serviceName));
|
||||
@@ -193,10 +195,12 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
clientConfig.set(CommonClientConfigKey.OkToRetryOnAllOperations, retryOnAllOps);
|
||||
clientConfig.set(CommonClientConfigKey.MaxAutoRetriesNextServer, retriesNextServer);
|
||||
clientConfig.set(CommonClientConfigKey.MaxAutoRetries, retriesSameServer);
|
||||
clientConfig.set(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES, statusCodes);
|
||||
clientConfig.setClientName(serviceName);
|
||||
RibbonLoadBalancerContext context = new RibbonLoadBalancerContext(lb, clientConfig, retryHandler);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(context).when(clientFactory).getLoadBalancerContext(eq(serviceName));
|
||||
doReturn(clientConfig).when(clientFactory).getClientConfig(eq(serviceName));
|
||||
LoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(clientConfig, introspector, factory);
|
||||
client.setLoadBalancer(lb);
|
||||
@@ -217,10 +221,13 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
doThrow(new IOException("boom")).doReturn(response).when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(method).when(request).getMethod();
|
||||
@@ -246,11 +253,14 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(method).when(request).getMethod();
|
||||
@@ -276,11 +286,14 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(uri).when(request).getURI();
|
||||
@@ -309,7 +322,7 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
serviceName, host, port, delegate, lb, "");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(uri).when(request).getURI();
|
||||
@@ -326,6 +339,42 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryOnStatusCode() throws Exception {
|
||||
int retriesNextServer = 0;
|
||||
int retriesSameServer = 1;
|
||||
boolean retryable = true;
|
||||
boolean retryOnAllOps = false;
|
||||
String serviceName = "foo";
|
||||
String host = serviceName;
|
||||
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);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(response).getStatusLine();
|
||||
final HttpResponse fourOFourResponse = mock(HttpResponse.class);
|
||||
StatusLine fourOFourStatusLine = mock(StatusLine.class);
|
||||
doReturn(404).when(fourOFourStatusLine).getStatusCode();
|
||||
doReturn(fourOFourStatusLine).when(fourOFourResponse).getStatusLine();
|
||||
doReturn(fourOFourResponse).doReturn(response).when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb, "404");
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(request).when(request).withNewUri(any(URI.class));
|
||||
HttpUriRequest uriRequest = mock(HttpUriRequest.class);
|
||||
doReturn(uri).when(uriRequest).getURI();
|
||||
doReturn(uriRequest).when(request).toRequest(any(RequestConfig.class));
|
||||
RibbonApacheHttpResponse returnedResponse = client.execute(request, null);
|
||||
verify(delegate, times(2)).execute(any(HttpUriRequest.class));
|
||||
verify(lb, times(0)).chooseServer(eq(serviceName));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class UseDefaults {
|
||||
|
||||
@@ -395,8 +444,12 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
|
||||
ReflectionTestUtils.setField(client, "delegate", delegate);
|
||||
ReflectionTestUtils.setField(client, "lb", loadBalancer);
|
||||
HttpResponse httpResponse = mock(HttpResponse.class);
|
||||
StatusLine statusLine = mock(StatusLine.class);
|
||||
doReturn(200).when(statusLine).getStatusCode();
|
||||
doReturn(statusLine).when(httpResponse).getStatusLine();
|
||||
given(delegate.execute(any(HttpUriRequest.class))).willReturn(
|
||||
mock(HttpResponse.class));
|
||||
httpResponse);
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(request).when(request).withNewUri(any(URI.class));
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
*
|
||||
* * 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.ribbon.support;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableStatusCodeExceptionTest {
|
||||
|
||||
@Test
|
||||
public void testMessage() {
|
||||
RetryableStatusCodeException ex = new RetryableStatusCodeException("foo", 404);
|
||||
assertEquals("Service foo returned a status code of 404", ex.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
"disableretry.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.globalretrydisabled: /globalretrydisabled/**",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetries: 1",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1"
|
||||
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"retryable.ribbon.retryableStatusCodes: 404,403"
|
||||
})
|
||||
@DirtiesContext
|
||||
public class HttpClientRibbonRetryIntegrationTests extends RibbonRetryIntegrationTestBase {
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
"zuul.routes.retryable: /retryable/**",
|
||||
"zuul.routes.retryable.retryable: true",
|
||||
"retryable.ribbon.OkToRetryOnAllOperations: true",
|
||||
"retryable.ribbon.retryableStatusCodes: 404",
|
||||
"retryable.ribbon.MaxAutoRetries: 1",
|
||||
"retryable.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.getretryable: /getretryable/**",
|
||||
|
||||
@@ -25,8 +25,15 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
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.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -37,6 +44,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
@@ -74,6 +82,15 @@ public abstract class RibbonRetryIntegrationTestBase {
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retryableFourOFour() {
|
||||
String uri = "/retryable/404everyothererror";
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRetryOK() {
|
||||
String uri = "/retryable/posteveryothererror";
|
||||
@@ -161,6 +178,17 @@ public abstract class RibbonRetryIntegrationTestBase {
|
||||
return timeout();
|
||||
}
|
||||
|
||||
@RequestMapping("/404everyothererror")
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public ResponseEntity<String> fourOFourError() {
|
||||
boolean shouldError = error;
|
||||
error = !error;
|
||||
if(shouldError) {
|
||||
return new ResponseEntity<String>("not found", HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return new ResponseEntity<String>("no error", HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -173,6 +201,46 @@ public abstract class RibbonRetryIntegrationTestBase {
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class FourOFourRetryableRibbonConfiguration extends RibbonClientConfiguration {
|
||||
|
||||
@Bean
|
||||
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory factory) {
|
||||
return new MyRibbonRetryPolicyFactory(factory);
|
||||
}
|
||||
|
||||
public static class MyRibbonRetryPolicyFactory extends RibbonLoadBalancedRetryPolicyFactory {
|
||||
|
||||
private SpringClientFactory factory;
|
||||
|
||||
public MyRibbonRetryPolicyFactory(SpringClientFactory clientFactory) {
|
||||
super(clientFactory);
|
||||
this.factory = clientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancedRetryPolicy create(String serviceId, ServiceInstanceChooser loadBalanceChooser) {
|
||||
RibbonLoadBalancerContext lbContext = this.factory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
return new MyLoadBalancedRetryPolicy(serviceId, lbContext, loadBalanceChooser);
|
||||
}
|
||||
|
||||
class MyLoadBalancedRetryPolicy extends RibbonLoadBalancedRetryPolicy {
|
||||
|
||||
public MyLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser) {
|
||||
super(serviceId, context, loadBalanceChooser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retryableStatusCode( int statusCode) {
|
||||
if(statusCode == HttpStatus.NOT_FOUND.value()) {
|
||||
return true;
|
||||
}
|
||||
return super.retryableStatusCode(statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user