Merging PR #1680 into master
This commit is contained in:
@@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
|
||||
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -126,9 +127,10 @@ public class RibbonClientConfiguration {
|
||||
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
|
||||
public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
|
||||
IClientConfig config, ServerIntrospector serverIntrospector,
|
||||
ILoadBalancer loadBalancer, RetryHandler retryHandler) {
|
||||
ILoadBalancer loadBalancer, RetryHandler retryHandler,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(
|
||||
config, serverIntrospector);
|
||||
config, serverIntrospector, loadBalancedRetryPolicyFactory);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
client.setRetryHandler(retryHandler);
|
||||
Monitors.registerObject("Client_" + this.name, client);
|
||||
@@ -147,9 +149,9 @@ public class RibbonClientConfiguration {
|
||||
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
|
||||
public OkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config,
|
||||
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
|
||||
RetryHandler retryHandler) {
|
||||
RetryHandler retryHandler, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config,
|
||||
serverIntrospector);
|
||||
serverIntrospector, loadBalancedRetryPolicyFactory);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
client.setRetryHandler(retryHandler);
|
||||
Monitors.registerObject("Client_" + this.name, client);
|
||||
|
||||
@@ -27,8 +27,14 @@ import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.impl.client.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.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 java.net.URI;
|
||||
@@ -37,11 +43,11 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
|
||||
|
||||
/**
|
||||
* @author Christian Lohmann
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
//TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient
|
||||
public class RibbonLoadBalancingHttpClient
|
||||
extends
|
||||
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> {
|
||||
extends RetryableLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> {
|
||||
|
||||
@Deprecated
|
||||
public RibbonLoadBalancingHttpClient() {
|
||||
@@ -61,6 +67,11 @@ public class RibbonLoadBalancingHttpClient
|
||||
super(delegate, config, serverIntrospector);
|
||||
}
|
||||
|
||||
public RibbonLoadBalancingHttpClient(IClientConfig iClientConfig, ServerIntrospector serverIntrospector,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
super(iClientConfig, serverIntrospector, loadBalancedRetryPolicyFactory);
|
||||
}
|
||||
|
||||
protected HttpClient createDelegate(IClientConfig config) {
|
||||
return HttpClientBuilder.create()
|
||||
// already defaults to 0 in builder, so resetting to 0 won't hurt
|
||||
@@ -73,7 +84,7 @@ public class RibbonLoadBalancingHttpClient
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,
|
||||
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
final RequestConfig.Builder builder = RequestConfig.custom();
|
||||
IClientConfig config = configOverride != null ? configOverride : this.config;
|
||||
@@ -85,16 +96,32 @@ public class RibbonLoadBalancingHttpClient
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
|
||||
final RequestConfig requestConfig = builder.build();
|
||||
|
||||
if (isSecure(configOverride)) {
|
||||
final URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
|
||||
.scheme("https").build().toUri();
|
||||
request = request.withNewUri(secureUri);
|
||||
}
|
||||
|
||||
final HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
|
||||
final HttpResponse httpResponse = this.delegate.execute(httpUriRequest);
|
||||
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
|
||||
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 = RibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
|
||||
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,10 +129,4 @@ public class RibbonLoadBalancingHttpClient
|
||||
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server);
|
||||
return super.reconstructURIWithServer(server, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
|
||||
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,13 @@ package org.springframework.cloud.netflix.ribbon.okhttp;
|
||||
import java.net.URI;
|
||||
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.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 com.netflix.client.config.CommonClientConfigKey;
|
||||
@@ -36,9 +41,10 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class OkHttpLoadBalancingClient
|
||||
extends AbstractLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {
|
||||
extends RetryableLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {
|
||||
|
||||
@Deprecated
|
||||
public OkHttpLoadBalancingClient() {
|
||||
@@ -55,6 +61,12 @@ public class OkHttpLoadBalancingClient
|
||||
super(config, serverIntrospector);
|
||||
}
|
||||
|
||||
public OkHttpLoadBalancingClient(IClientConfig config,
|
||||
ServerIntrospector serverIntrospector,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
super(config, serverIntrospector, loadBalancedRetryPolicyFactory);
|
||||
}
|
||||
|
||||
public OkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config,
|
||||
ServerIntrospector serverIntrospector) {
|
||||
super(delegate, config, serverIntrospector);
|
||||
@@ -66,21 +78,36 @@ public class OkHttpLoadBalancingClient
|
||||
}
|
||||
|
||||
@Override
|
||||
public OkHttpRibbonResponse execute(OkHttpRibbonRequest ribbonRequest,
|
||||
public OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
boolean secure = isSecure(configOverride);
|
||||
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);
|
||||
|
||||
if (secure) {
|
||||
final URI secureUri = UriComponentsBuilder.fromUri(ribbonRequest.getUri())
|
||||
.scheme("https").build().toUri();
|
||||
ribbonRequest = ribbonRequest.withNewUri(secureUri);
|
||||
}
|
||||
|
||||
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
|
||||
|
||||
final Request request = ribbonRequest.toRequest();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
return new OkHttpRibbonResponse(response, ribbonRequest.getUri());
|
||||
final Request request = newRequest.toRequest();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
return new OkHttpRibbonResponse(response, newRequest.getUri());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
OkHttpClient getOkHttpClient(IClientConfig configOverride, boolean secure) {
|
||||
|
||||
@@ -18,18 +18,29 @@
|
||||
package org.springframework.cloud.netflix.ribbon.support;
|
||||
|
||||
import com.netflix.client.ClientRequest;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class ContextAwareRequest extends ClientRequest {
|
||||
public abstract class ContextAwareRequest extends ClientRequest implements HttpRequest {
|
||||
protected final RibbonCommandContext context;
|
||||
private HttpHeaders httpHeaders;
|
||||
|
||||
public ContextAwareRequest(RibbonCommandContext context) {
|
||||
this.context = context;
|
||||
MultiValueMap<String, String> headers = context.getHeaders();
|
||||
this.httpHeaders = new HttpHeaders();
|
||||
for(String key : headers.keySet()) {
|
||||
this.httpHeaders.put(key, headers.get(key));
|
||||
}
|
||||
this.uri = context.uri();
|
||||
this.isRetriable = context.getRetryable();
|
||||
}
|
||||
@@ -38,6 +49,21 @@ public abstract class ContextAwareRequest extends ClientRequest {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.valueOf(context.getMethod());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return this.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
protected RibbonCommandContext newContext(URI uri) {
|
||||
RibbonCommandContext commandContext = new RibbonCommandContext(this.context.getServiceId(),
|
||||
this.context.getMethod(), uri.toString(), this.context.getRetryable(),
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
|
||||
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.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import com.netflix.client.IResponse;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
/**
|
||||
* A load balancing client which uses Spring Retry to retry failed requests.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class RetryableLoadBalancingClient<S extends ContextAwareRequest, T extends IResponse, D>
|
||||
extends AbstractLoadBalancingClient<S, T, D> implements ServiceInstanceChooser {
|
||||
|
||||
protected LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory =
|
||||
new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
|
||||
|
||||
@Deprecated
|
||||
public RetryableLoadBalancingClient() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public RetryableLoadBalancingClient(final ILoadBalancer lb) {
|
||||
super(lb);
|
||||
}
|
||||
|
||||
public RetryableLoadBalancingClient(IClientConfig config, ServerIntrospector 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a {@link S} using Spring Retry.
|
||||
* @param request The request to execute.
|
||||
* @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);
|
||||
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(S 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,36 +16,70 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.apache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Sébastien Nussbaumer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonLoadBalancingHttpClientTests {
|
||||
|
||||
private ILoadBalancer loadBalancer;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
loadBalancer = mock(AbstractLoadBalancer.class);
|
||||
doReturn(new Server("foo.com", 8000)).when(loadBalancer).chooseServer(eq("default"));
|
||||
doReturn(new Server("foo.com", 8000)).when(loadBalancer).chooseServer(eq("service"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
loadBalancer = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestConfigUseDefaultsNoOverride() throws Exception {
|
||||
RequestConfig result = getBuiltRequestConfig(UseDefaults.class, null);
|
||||
@@ -129,6 +163,169 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
assertThat(result.getSocketTimeout(), is (50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNeverRetry() throws Exception {
|
||||
ServerIntrospector introspector = mock(ServerIntrospector.class);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
HttpResponse response = mock(HttpResponse.class);
|
||||
doThrow(new IOException("boom")).when(delegate).execute(any(HttpUriRequest.class));
|
||||
DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
|
||||
clientConfig.setClientName("foo");
|
||||
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(delegate, clientConfig,
|
||||
introspector);
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
try {
|
||||
client.execute(request, null);
|
||||
fail("Expected IOException");
|
||||
} catch(IOException e) {} finally {
|
||||
verify(delegate, times(1)).execute(any(HttpUriRequest.class));
|
||||
}
|
||||
}
|
||||
|
||||
private RibbonLoadBalancingHttpClient setupClientForRetry(int retriesNextServer, int retriesSameServer,
|
||||
boolean retryable, boolean retryOnAllOps,
|
||||
String serviceName, String host, int port,
|
||||
HttpClient delegate, ILoadBalancer lb) throws Exception {
|
||||
ServerIntrospector introspector = mock(ServerIntrospector.class);
|
||||
RetryHandler retryHandler = new DefaultLoadBalancerRetryHandler(retriesSameServer, retriesNextServer, retryable);
|
||||
doReturn(new Server(host, port)).when(lb).chooseServer(eq(serviceName));
|
||||
DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
|
||||
clientConfig.set(CommonClientConfigKey.OkToRetryOnAllOperations, retryOnAllOps);
|
||||
clientConfig.set(CommonClientConfigKey.MaxAutoRetriesNextServer, retriesNextServer);
|
||||
clientConfig.set(CommonClientConfigKey.MaxAutoRetries, retriesSameServer);
|
||||
clientConfig.setClientName(serviceName);
|
||||
RibbonLoadBalancerContext context = new RibbonLoadBalancerContext(lb, clientConfig, retryHandler);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(context).when(clientFactory).getLoadBalancerContext(eq(serviceName));
|
||||
LoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(clientConfig, introspector, factory);
|
||||
client.setLoadBalancer(lb);
|
||||
ReflectionTestUtils.setField(client, "delegate", delegate);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrySameServerOnly() 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);
|
||||
doThrow(new IOException("boom")).doReturn(response).when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryNextServer() throws Exception {
|
||||
int retriesNextServer = 1;
|
||||
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);
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
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(3)).execute(any(HttpUriRequest.class));
|
||||
verify(lb, times(1)).chooseServer(eq(serviceName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryOnPost() throws Exception {
|
||||
int retriesNextServer = 1;
|
||||
int retriesSameServer = 1;
|
||||
boolean retryable = true;
|
||||
boolean retryOnAllOps = true;
|
||||
String serviceName = "foo";
|
||||
String host = serviceName;
|
||||
int port = 80;
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(request).when(request).withNewUri(any(URI.class));
|
||||
HttpUriRequest uriRequest = mock(HttpUriRequest.class);
|
||||
doReturn(uriRequest).when(request).toRequest(any(RequestConfig.class));
|
||||
RibbonApacheHttpResponse returnedResponse = client.execute(request, null);
|
||||
verify(delegate, times(3)).execute(any(HttpUriRequest.class));
|
||||
verify(lb, times(1)).chooseServer(eq(serviceName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoRetryOnPost() throws Exception {
|
||||
int retriesNextServer = 1;
|
||||
int retriesSameServer = 1;
|
||||
boolean retryable = true;
|
||||
boolean retryOnAllOps = false;
|
||||
String serviceName = "foo";
|
||||
String host = serviceName;
|
||||
int port = 80;
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
final HttpResponse response = mock(HttpResponse.class);
|
||||
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
|
||||
when(delegate).execute(any(HttpUriRequest.class));
|
||||
ILoadBalancer lb = mock(ILoadBalancer.class);
|
||||
RibbonLoadBalancingHttpClient client = setupClientForRetry(retriesNextServer, retriesSameServer, retryable, retryOnAllOps,
|
||||
serviceName, host, port, delegate, lb);
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(method).when(request).getMethod();
|
||||
doReturn(uri).when(request).getURI();
|
||||
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));
|
||||
try {
|
||||
client.execute(request, null);
|
||||
fail("Expected IOException");
|
||||
} catch(IOException e) {} finally {
|
||||
verify(delegate, times(1)).execute(any(HttpUriRequest.class));
|
||||
verify(lb, times(0)).chooseServer(eq(serviceName));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class UseDefaults {
|
||||
|
||||
@@ -188,14 +385,21 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
|
||||
factory.setApplicationContext(new AnnotationConfigApplicationContext(
|
||||
RibbonAutoConfiguration.class, defaultConfigurationClass));
|
||||
String serviceName = "foo";
|
||||
String host = serviceName;
|
||||
int port = 80;
|
||||
URI uri = new URI("http://" + host + ":" + port);
|
||||
HttpClient delegate = mock(HttpClient.class);
|
||||
RibbonLoadBalancingHttpClient client = factory.getClient("service",
|
||||
RibbonLoadBalancingHttpClient.class);
|
||||
|
||||
ReflectionTestUtils.setField(client, "delegate", delegate);
|
||||
ReflectionTestUtils.setField(client, "lb", loadBalancer);
|
||||
given(delegate.execute(any(HttpUriRequest.class))).willReturn(
|
||||
mock(HttpResponse.class));
|
||||
RibbonApacheHttpRequest request = mock(RibbonApacheHttpRequest.class);
|
||||
doReturn(uri).when(request).getURI();
|
||||
doReturn(request).when(request).withNewUri(any(URI.class));
|
||||
given(request.toRequest(any(RequestConfig.class))).willReturn(
|
||||
mock(HttpUriRequest.class));
|
||||
|
||||
@@ -203,7 +407,7 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
|
||||
ArgumentCaptor<RequestConfig> requestConfigCaptor = ArgumentCaptor
|
||||
.forClass(RequestConfig.class);
|
||||
verify(request).toRequest(requestConfigCaptor.capture());
|
||||
verify(request, times(1)).toRequest(requestConfigCaptor.capture());
|
||||
return requestConfigCaptor.getValue();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
*
|
||||
* * 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.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class ContextAwareRequestTest {
|
||||
|
||||
private RibbonCommandContext context;
|
||||
private ContextAwareRequest request;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
context = mock(RibbonCommandContext.class);
|
||||
doReturn("GET").when(context).getMethod();
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.put("header1", Collections.<String>emptyList());
|
||||
headers.put("header2", Arrays.asList("value1", "value2"));
|
||||
headers.put("header3", Arrays.asList("value1"));
|
||||
doReturn(headers).when(context).getHeaders();
|
||||
doReturn(new URI("http://foo")).when(context).uri();
|
||||
doReturn("foo").when(context).getServiceId();
|
||||
doReturn(new LinkedMultiValueMap<>()).when(context).getParams();
|
||||
request = new TestContextAwareRequest(context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
context = null;
|
||||
request = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContext() throws Exception {
|
||||
assertEquals(context, request.getContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMethod() throws Exception {
|
||||
assertEquals(HttpMethod.GET, request.getMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getURI() throws Exception {
|
||||
assertEquals(new URI("http://foo"), request.getURI());
|
||||
|
||||
RibbonCommandContext badUriContext = mock(RibbonCommandContext.class);
|
||||
doReturn(new LinkedMultiValueMap()).when(badUriContext).getHeaders();
|
||||
doReturn("foobar").when(badUriContext).getUri();
|
||||
ContextAwareRequest badUriRequest = new TestContextAwareRequest(badUriContext);
|
||||
|
||||
assertNull(badUriRequest.getURI());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHeaders() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.put("header1", Collections.<String>emptyList());
|
||||
headers.put("header2", Arrays.asList("value1", "value2"));
|
||||
headers.put("header3", Arrays.asList("value1"));
|
||||
assertEquals(headers, request.getHeaders());
|
||||
}
|
||||
|
||||
static class TestContextAwareRequest extends ContextAwareRequest {
|
||||
|
||||
public TestContextAwareRequest(RibbonCommandContext context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
*
|
||||
* * 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.zuul.filters.route.apache;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonRetryIntegrationTestBase;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = RibbonRetryIntegrationTestBase.RetryableTestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
|
||||
"zuul.retryable: false", /* Disable retry by default, have each route enable it */
|
||||
"hystrix.command.default.execution.timeout.enabled: false", /* Disable hystrix so its timeout doesnt get in the way */
|
||||
"ribbon.ReadTimeout: 1000", /* Make sure ribbon will timeout before the thread is done sleeping */
|
||||
"zuul.routes.retryable: /retryable/**",
|
||||
"zuul.routes.retryable.retryable: true",
|
||||
"retryable.ribbon.OkToRetryOnAllOperations: true",
|
||||
"retryable.ribbon.MaxAutoRetries: 1",
|
||||
"retryable.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.getretryable: /getretryable/**",
|
||||
"zuul.routes.getretryable.retryable: true",
|
||||
"getretryable.ribbon.MaxAutoRetries: 1",
|
||||
"getretryable.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.disableretry: /disableretry/**",
|
||||
"zuul.routes.disableretry.retryable: false", /* This will override the global */
|
||||
"disableretry.ribbon.MaxAutoRetries: 1",
|
||||
"disableretry.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.globalretrydisabled: /globalretrydisabled/**",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetries: 1",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1"
|
||||
})
|
||||
@DirtiesContext
|
||||
public class HttpClientRibbonRetryIntegrationTests extends RibbonRetryIntegrationTestBase {
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
*
|
||||
* * 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.zuul.filters.route.okhttp;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonRetryIntegrationTestBase;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = RibbonRetryIntegrationTestBase.RetryableTestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
|
||||
"zuul.retryable: false", /* Disable retry by default, have each route enable it */
|
||||
"ribbon.okhttp.enabled: true",
|
||||
"hystrix.command.default.execution.timeout.enabled: false", /* Disable hystrix so its timeout doesnt get in the way */
|
||||
"ribbon.ReadTimeout: 1000", /* Make sure ribbon will timeout before the thread is done sleeping */
|
||||
"zuul.routes.retryable: /retryable/**",
|
||||
"zuul.routes.retryable.retryable: true",
|
||||
"retryable.ribbon.OkToRetryOnAllOperations: true",
|
||||
"retryable.ribbon.MaxAutoRetries: 1",
|
||||
"retryable.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.getretryable: /getretryable/**",
|
||||
"zuul.routes.getretryable.retryable: true",
|
||||
"getretryable.ribbon.MaxAutoRetries: 1",
|
||||
"getretryable.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.disableretry: /disableretry/**",
|
||||
"zuul.routes.disableretry.retryable: false", /* This will override the global */
|
||||
"disableretry.ribbon.MaxAutoRetries: 1",
|
||||
"disableretry.ribbon.MaxAutoRetriesNextServer: 1",
|
||||
"zuul.routes.globalretrydisabled: /globalretrydisabled/**",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetries: 1",
|
||||
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1"
|
||||
})
|
||||
@DirtiesContext
|
||||
public class OkHttpRibbonRetryIntegrationTests extends RibbonRetryIntegrationTestBase {
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
*
|
||||
* * 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.zuul.filters.route.support;
|
||||
|
||||
import org.junit.Before;
|
||||
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.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class RibbonRetryIntegrationTestBase {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
protected int port;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
String uri = "/resetError";
|
||||
new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void retryable() {
|
||||
String uri = "/retryable/everyothererror";
|
||||
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";
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.POST,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRetryable() {
|
||||
String uri = "/getretryable/everyothererror";
|
||||
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 postNotRetryable() {
|
||||
String uri = "/getretryable/posteveryothererror";
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.POST,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disbaleRetry() {
|
||||
String uri = "/disableretry/everyothererror";
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void globalRetryDisabled() {
|
||||
String uri = "/globalretrydisabled/everyothererror";
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
|
||||
}
|
||||
|
||||
// Don't use @SpringBootApplication because we don't want to component scan
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableZuulProxy
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "retryable", configuration = RibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "disableretry", configuration = RibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "globalretrydisabled", configuration = RibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "getretryable", configuration = RibbonClientConfiguration.class)})
|
||||
public static class RetryableTestConfig {
|
||||
|
||||
private boolean error = true;
|
||||
|
||||
@RequestMapping("/resetError")
|
||||
public void resetError() {
|
||||
error = true;
|
||||
}
|
||||
|
||||
@RequestMapping("/everyothererror")
|
||||
public ResponseEntity<String> timeout() {
|
||||
boolean shouldError = error;
|
||||
error = !error;
|
||||
try {
|
||||
if(shouldError) {
|
||||
Thread.sleep(80000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
return new ResponseEntity<String>("no error", HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/posteveryothererror", method = RequestMethod.POST)
|
||||
public ResponseEntity<String> postTimeout() {
|
||||
return timeout();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class RibbonClientConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -375,7 +375,6 @@ public abstract class ZuulProxyTestBase {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "slow";
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -506,8 +505,8 @@ public abstract class ZuulProxyTestBase {
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -123,5 +123,10 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
Reference in New Issue
Block a user