Merge remote-tracking branch 'origin/master' into 2.0.x

This commit is contained in:
Ryan Baxter
2017-09-25 14:18:49 -04:00
23 changed files with 467 additions and 283 deletions

View File

@@ -38,7 +38,6 @@ Example eureka client:
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@RestController
public class Application {
@@ -54,9 +53,8 @@ public class Application {
}
----
(i.e. utterly normal Spring Boot app). In this example we use
`@EnableEurekaClient` explicitly, but with only Eureka available you
could also use `@EnableDiscoveryClient`. Configuration is required to
(i.e. utterly normal Spring Boot app). By having `spring-cloud-starter-netflix-eureka-client`
on the classpath your application will automatically register with the Eureka Server. Configuration is required to
locate the Eureka server. Example:
@@ -76,7 +74,8 @@ The default application name (service ID), virtual host and non-secure
port, taken from the `Environment`, are `${spring.application.name}`,
`${spring.application.name}` and `${server.port}` respectively.
`@EnableEurekaClient` makes the app into both a Eureka "instance"
Having `spring-cloud-starter-netflix-eureka-client` on the classpath
makes the app into both a Eureka "instance"
(i.e. it registers itself) and a "client" (i.e. it can query the
registry to locate other services). The instance behaviour is driven
by `eureka.instance.*` configuration keys, but the defaults will be
@@ -86,6 +85,8 @@ ID, or VIP).
See {github-code}/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBean.java[EurekaInstanceConfigBean] and {github-code}/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientConfigBean.java[EurekaClientConfigBean] for more details of the configurable options.
To disable the Eureka Discovery Client you can set `eureka.client.enabled` to `false`.
=== Authenticating with the Eureka Server
HTTP basic authentication will be automatically added to your eureka
@@ -240,7 +241,7 @@ random value will not be needed.
=== Using the EurekaClient
Once you have an app that is `@EnableDiscoveryClient` (or `@EnableEurekaClient`) you can use it to
Once you have an app that is a discovery client you can use it to
discover service instances from the <<spring-cloud-eureka-server,
Eureka Server>>. One way to do that is to use the native
`com.netflix.discovery.EurekaClient` (as opposed to the Spring
@@ -971,7 +972,6 @@ Example spring boot app
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@EnableFeignClients
public class Application {
@@ -2656,6 +2656,9 @@ 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.
WARNING: Enabling `client.ribbon.OkToRetryOnAllOperations` includes retring POST requests wich can have a impact
on the server's resources due to the buffering of the request's body.
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

View File

@@ -275,8 +275,10 @@ public class SpringMvcContract extends Contract.BaseContract
if (annotation.headers() != null && annotation.headers().length > 0) {
for (String header : annotation.headers()) {
int index = header.indexOf('=');
md.template().header(resolve(header.substring(0, index)),
if (!header.contains("!=") && index >= 0) {
md.template().header(resolve(header.substring(0, index)),
resolve(header.substring(index + 1).trim()));
}
}
}
}

View File

@@ -22,7 +22,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Import;
/**
@@ -37,7 +36,6 @@ import org.springframework.context.annotation.Import;
* @author Biju Kunjummen
*/
@EnableCircuitBreaker
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)

View File

@@ -157,30 +157,15 @@ public class ProxyRequestHelper {
context.setResponseDataStream(entity);
}
HttpHeaders httpHeaders = new HttpHeaders();
for (Entry<String, List<String>> header : headers.entrySet()) {
List<String> values = header.getValue();
for (String value : values) {
httpHeaders.add(header.getKey(), value);
}
}
boolean isOriginResponseGzipped = false;
if (httpHeaders.containsKey(CONTENT_ENCODING)) {
List<String> collection = httpHeaders.get(CONTENT_ENCODING);
for (String header : collection) {
if (HTTPRequestUtils.getInstance().isGzipped(header)) {
isOriginResponseGzipped = true;
break;
}
}
}
context.setResponseGZipped(isOriginResponseGzipped);
for (Entry<String, List<String>> header : headers.entrySet()) {
String name = header.getKey();
for (String value : header.getValue()) {
context.addOriginResponseHeader(name, value);
if (name.equalsIgnoreCase(CONTENT_LENGTH)) {
if (name.equalsIgnoreCase(HttpHeaders.CONTENT_ENCODING)
&& HTTPRequestUtils.getInstance().isGzipped(value)) {
isOriginResponseGzipped = true;
}
if (name.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
context.setOriginContentLength(value);
}
if (isIncludedHeader(name)) {
@@ -188,6 +173,7 @@ public class ProxyRequestHelper {
}
}
}
context.setResponseGZipped(isOriginResponseGzipped);
}
public void addIgnoredHeaders(String... names) {

View File

@@ -16,6 +16,13 @@
package org.springframework.cloud.netflix.zuul.filters.route;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.filters.support.ResettableServletInputStreamWrapper;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StreamUtils;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
@@ -23,11 +30,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
/**
* @author Spencer Gibb
*/
@@ -38,26 +40,27 @@ public class RibbonCommandContext {
private final Boolean retryable;
private final MultiValueMap<String, String> headers;
private final MultiValueMap<String, String> params;
private final InputStream requestEntity;
private final List<RibbonRequestCustomizer> requestCustomizers;
private InputStream requestEntity;
private Long contentLength;
/**
* Kept for backwards compatibility with Spring Cloud Sleuth 1.x versions
*/
@Deprecated
public RibbonCommandContext(String serviceId, String method, String uri,
Boolean retryable, MultiValueMap<String, String> headers,
MultiValueMap<String, String> params, InputStream requestEntity) {
public RibbonCommandContext(String serviceId, String method,
String uri, Boolean retryable, MultiValueMap<String, String> headers,
MultiValueMap<String, String> params, InputStream requestEntity) {
this(serviceId, method, uri, retryable, headers, params, requestEntity,
new ArrayList<RibbonRequestCustomizer>(), null);
new ArrayList<RibbonRequestCustomizer>(), null);
}
public RibbonCommandContext(String serviceId, String method, String uri,
Boolean retryable, MultiValueMap<String, String> headers,
MultiValueMap<String, String> params, InputStream requestEntity,
List<RibbonRequestCustomizer> requestCustomizers) {
this(serviceId, method, uri, retryable, headers, params, requestEntity, requestCustomizers, null);
this(serviceId, method, uri, retryable, headers, params, requestEntity,
requestCustomizers, null);
}
public RibbonCommandContext(String serviceId, String method, String uri,
@@ -84,8 +87,7 @@ public class RibbonCommandContext {
public URI uri() {
try {
return new URI(this.uri);
}
catch (URISyntaxException e) {
} catch (URISyntaxException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return null;
@@ -93,6 +95,7 @@ public class RibbonCommandContext {
/**
* Use getMethod()
*
* @return
*/
@Deprecated
@@ -125,7 +128,19 @@ public class RibbonCommandContext {
}
public InputStream getRequestEntity() {
return requestEntity;
if (requestEntity == null) {
return requestEntity;
}
try {
if (!(requestEntity instanceof ResettableServletInputStreamWrapper)) {
requestEntity = new ResettableServletInputStreamWrapper(
StreamUtils.copyToByteArray(requestEntity));
}
requestEntity.reset();
} finally {
return requestEntity;
}
}
public List<RibbonRequestCustomizer> getRequestCustomizers() {
@@ -142,23 +157,25 @@ public class RibbonCommandContext {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
RibbonCommandContext that = (RibbonCommandContext) o;
return Objects.equals(serviceId, that.serviceId) &&
Objects.equals(method, that.method) &&
Objects.equals(uri, that.uri) &&
Objects.equals(retryable, that.retryable) &&
Objects.equals(headers, that.headers) &&
Objects.equals(params, that.params) &&
Objects.equals(requestEntity, that.requestEntity) &&
Objects.equals(requestCustomizers, that.requestCustomizers) &&
Objects.equals(contentLength, that.contentLength);
return Objects.equals(serviceId, that.serviceId) && Objects
.equals(method, that.method) && Objects.equals(uri, that.uri)
&& Objects.equals(retryable, that.retryable) && Objects
.equals(headers, that.headers) && Objects
.equals(params, that.params) && Objects
.equals(requestEntity, that.requestEntity) && Objects
.equals(requestCustomizers, that.requestCustomizers) && Objects
.equals(contentLength, that.contentLength);
}
@Override
public int hashCode() {
return Objects.hash(serviceId, method, uri, retryable, headers, params, requestEntity, requestCustomizers, contentLength);
return Objects.hash(serviceId, method, uri, retryable, headers, params,
requestEntity, requestCustomizers, contentLength);
}
@Override

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.zuul.filters.support;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class ResettableServletInputStreamWrapper extends ServletInputStream {
private final ByteArrayInputStream input;
public ResettableServletInputStreamWrapper(byte[] data) {
this.input = new ByteArrayInputStream(data);
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return input.read();
}
@Override
public synchronized void reset() throws IOException {
input.reset();
}
}

View File

@@ -21,8 +21,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.cloud.netflix.zuul.FormZuulServletProxyApplicationTests;
import org.springframework.cloud.netflix.zuul.filters.route.LazyLoadOfZuulConfigurationTests;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandCauseFallbackPropagationTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandHystrixThreadPoolKeyTests;
/**
* A test suite for probing weird ordering problems in the tests.
@@ -30,8 +30,8 @@ import org.springframework.cloud.netflix.zuul.filters.route.LazyLoadOfZuulConfig
* @author Dave Syer
*/
@RunWith(Suite.class)
@SuiteClasses({ FormZuulServletProxyApplicationTests.class,
LazyLoadOfZuulConfigurationTests.class })
@SuiteClasses({ RibbonCommandHystrixThreadPoolKeyTests.class,
RibbonCommandCauseFallbackPropagationTest.class })
@Ignore
public class AdhocTestSuite {

View File

@@ -332,6 +332,18 @@ public class SpringMvcContractTests {
assertEquals("bar", data.template().headers().get("X-Foo").iterator().next());
}
@Test
public void testProcessHeadersWithoutValues() throws Exception {
Method method = TestTemplate_HeadersWithoutValues.class.getDeclaredMethod("getTest",
String.class);
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertEquals("/test/{id}", data.template().url());
assertEquals("GET", data.template().method());
assertEquals(true, data.template().headers().isEmpty());
}
@Test
public void testProcessAnnotations_Fallback() throws Exception {
Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTestFallback",
@@ -462,6 +474,11 @@ public class SpringMvcContractTests {
ResponseEntity<TestObject> getTest(@PathVariable("id") String id);
}
public interface TestTemplate_HeadersWithoutValues {
@RequestMapping(value = "/test/{id}", method = RequestMethod.GET, headers = { "X-Foo", "!X-Bar", "X-Baz!=fooBar" })
ResponseEntity<TestObject> getTest(@PathVariable("id") String id);
}
public interface TestTemplate_ListParams {
@RequestMapping(value = "/test", method = RequestMethod.GET)
ResponseEntity<TestObject> getTest(@RequestParam("id") List<String> id);

View File

@@ -89,7 +89,6 @@ public class ProxyRequestHelperTests {
helper.debug("POST", "http://example.com", headers,
new LinkedMultiValueMap<String, String>(), request.getInputStream());
Trace actual = this.traceRepository.findAll().get(0);
System.err.println(actual.getInfo());
assertThat((String) actual.getInfo().get("body"), equalTo("{}"));
}

View File

@@ -0,0 +1,78 @@
package org.springframework.cloud.netflix.zuul.filters.route;
import com.google.common.collect.Lists;
import okhttp3.Request;
import org.junit.Test;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.filters.support.ResettableServletInputStreamWrapper;
import org.springframework.http.HttpMethod;
import org.springframework.util.LinkedMultiValueMap;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Andre Dörnbrack
*/
public class RibbonCommandContextTest {
private static final byte[] TEST_CONTENT = { 42, 42, 42, 42, 42 };
private RibbonCommandContext ribbonCommandContext;
@Test
public void testMultipleReadsOnRequestEntity() throws Exception {
givenRibbonCommandContextIsSetup();
InputStream requestEntity = ribbonCommandContext.getRequestEntity();
assertTrue(requestEntity instanceof ResettableServletInputStreamWrapper);
whenInputStreamIsConsumed(requestEntity);
assertEquals(-1, requestEntity.read());
requestEntity.reset();
assertNotEquals(-1, requestEntity.read());
whenInputStreamIsConsumed(requestEntity);
assertEquals(-1, requestEntity.read());
requestEntity.reset();
assertNotEquals(-1, requestEntity.read());
whenInputStreamIsConsumed(requestEntity);
assertEquals(-1, requestEntity.read());
}
private void whenInputStreamIsConsumed(InputStream requestEntity) throws IOException {
while (requestEntity.read() != -1) {
requestEntity.read();
}
}
private void givenRibbonCommandContextIsSetup() {
LinkedMultiValueMap headers = new LinkedMultiValueMap();
LinkedMultiValueMap params = new LinkedMultiValueMap();
RibbonRequestCustomizer requestCustomizer = new RibbonRequestCustomizer<Request.Builder>() {
@Override
public boolean accepts(Class builderClass) {
return builderClass == Request.Builder.class;
}
@Override
public void customize(Request.Builder builder) {
builder.addHeader("from-customizer", "foo");
}
};
ribbonCommandContext = new RibbonCommandContext("serviceId",
HttpMethod.POST.toString(), "/my/route", true, headers, params,
new ByteArrayInputStream(TEST_CONTENT),
Lists.newArrayList(requestCustomizer));
}
}

View File

@@ -17,6 +17,11 @@
package org.springframework.cloud.netflix.zuul.filters.route.support;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientException;
import com.netflix.client.ClientRequest;
@@ -26,18 +31,16 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.client.http.HttpResponse;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.exception.HystrixTimeoutException;
import org.junit.Test;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -45,220 +48,241 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class RibbonCommandCauseFallbackPropagationTest {
@Test
public void providerIsCalledInCaseOfException() throws Exception {
TestZuulFallbackProviderWithoutCause provider = new TestZuulFallbackProviderWithoutCause(HttpStatus.INTERNAL_SERVER_ERROR);
RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception), provider);
@Test
public void providerIsCalledInCaseOfException() throws Exception {
TestZuulFallbackProviderWithoutCause provider = new TestZuulFallbackProviderWithoutCause(
HttpStatus.INTERNAL_SERVER_ERROR);
RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception),
provider);
ClientHttpResponse response = testCommand.execute();
ClientHttpResponse response = testCommand.execute();
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
}
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void causeIsProvidedForNewInterface() throws Exception {
TestFallbackProvider provider = TestFallbackProvider.withResponse(HttpStatus.NOT_FOUND);
RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception), provider);
@Test
public void causeIsProvidedForNewInterface() throws Exception {
TestFallbackProvider provider = TestFallbackProvider
.withResponse(HttpStatus.NOT_FOUND);
RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception),
provider);
ClientHttpResponse response = testCommand.execute();
ClientHttpResponse response = testCommand.execute();
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
Throwable cause = provider.getCause();
assertThat(cause.getClass()).isEqualTo(exception.getClass());
assertThat(cause.getMessage()).isEqualTo(exception.getMessage());
}
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
Throwable cause = provider.getCause();
assertThat(cause.getClass()).isEqualTo(exception.getClass());
assertThat(cause.getMessage()).isEqualTo(exception.getMessage());
}
@Test
public void executionExceptionIsUsedInsteadWhenFailedExceptionIsNull() throws Exception {
TestFallbackProvider provider = TestFallbackProvider.withResponse(HttpStatus.BAD_GATEWAY);
final RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception), provider) {
@Override
public Throwable getFailedExecutionException() {
return null;
}
@Test
public void executionExceptionIsUsedInsteadWhenFailedExceptionIsNull()
throws Exception {
TestFallbackProvider provider = TestFallbackProvider
.withResponse(HttpStatus.BAD_GATEWAY);
final RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception),
provider) {
@Override
public Throwable getFailedExecutionException() {
return null;
}
@Override
public Throwable getExecutionException() {
return exception;
}
};
@Override
public Throwable getExecutionException() {
return exception;
}
};
ClientHttpResponse response = testCommand.execute();
ClientHttpResponse response = testCommand.execute();
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_GATEWAY);
}
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_GATEWAY);
}
@Test
public void timeoutExceptionIsPropagated() throws Exception {
TestFallbackProvider provider = TestFallbackProvider.withResponse(HttpStatus.CONFLICT);
RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception), provider, 1) {
@Override
protected ClientRequest createRequest() throws Exception {
Thread.sleep(5);
return super.createRequest();
}
};
@Test
public void timeoutExceptionIsPropagated() throws Exception {
TestFallbackProvider provider = TestFallbackProvider
.withResponse(HttpStatus.CONFLICT);
RuntimeException exception = new RuntimeException("Failed!");
TestRibbonCommand testCommand = new TestRibbonCommand(new TestClient(exception),
provider, 1) {
@Override
protected ClientRequest createRequest() throws Exception {
Thread.sleep(5);
return super.createRequest();
}
};
ClientHttpResponse response = testCommand.execute();
ClientHttpResponse response = testCommand.execute();
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CONFLICT);
assertThat(provider.getCause()).isNotNull();
assertThat(provider.getCause().getClass()).isEqualTo(HystrixTimeoutException.class);
}
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CONFLICT);
assertThat(provider.getCause()).isNotNull();
assertThat(provider.getCause().getClass())
.isEqualTo(HystrixTimeoutException.class);
}
public static class TestRibbonCommand
extends AbstractRibbonCommand<AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse>, ClientRequest, HttpResponse> {
public static class TestRibbonCommand extends
AbstractRibbonCommand<AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse>, ClientRequest, HttpResponse> {
public TestRibbonCommand(AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse> client, ZuulFallbackProvider fallbackProvider) {
this(client, new ZuulProperties(), fallbackProvider);
}
public TestRibbonCommand(
AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse> client,
ZuulFallbackProvider fallbackProvider) {
this(client, new ZuulProperties(), fallbackProvider);
}
public TestRibbonCommand(AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse> client,
ZuulProperties zuulProperties,
ZuulFallbackProvider fallbackProvider) {
super("testCommand", client, null, zuulProperties, fallbackProvider);
}
public TestRibbonCommand(
AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse> client,
ZuulProperties zuulProperties, ZuulFallbackProvider fallbackProvider) {
super("testCommand" + UUID.randomUUID(), client, null, zuulProperties,
fallbackProvider);
}
public TestRibbonCommand(AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse> client,
ZuulFallbackProvider fallbackProvider,
int timeout) {
// different name is used because of properties caching
super(getSetter("testCommand2", new ZuulProperties()).andCommandPropertiesDefaults(defauts(timeout)),
client, null, fallbackProvider, null);
}
public TestRibbonCommand(
AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse> client,
ZuulFallbackProvider fallbackProvider, int timeout) {
// different name is used because of properties caching
super(getSetter("testCommand" + UUID.randomUUID(), new ZuulProperties())
.andCommandPropertiesDefaults(defauts(timeout)), client, null,
fallbackProvider, null);
}
private static HystrixCommandProperties.Setter defauts(final int timeout) {
return HystrixCommandProperties.Setter().withExecutionTimeoutEnabled(true)
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
.withExecutionTimeoutInMilliseconds(timeout);
}
private static HystrixCommandProperties.Setter defauts(final int timeout) {
return HystrixCommandProperties.Setter().withExecutionTimeoutEnabled(true)
.withExecutionIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
.withExecutionTimeoutInMilliseconds(timeout);
}
@Override
protected ClientRequest createRequest() throws Exception {
return null;
}
}
@Override
protected ClientRequest createRequest() throws Exception {
return null;
}
}
public static class TestClient extends AbstractLoadBalancerAwareClient {
@SuppressWarnings("rawtypes")
public static class TestClient extends AbstractLoadBalancerAwareClient {
private final RuntimeException exception;
private final RuntimeException exception;
public TestClient(RuntimeException exception) {
super(null);
this.exception = exception;
}
public TestClient(RuntimeException exception) {
super(null);
this.exception = exception;
}
@Override
public IResponse executeWithLoadBalancer(final ClientRequest request, final IClientConfig requestConfig) throws ClientException {
throw exception;
}
@Override
public IResponse executeWithLoadBalancer(final ClientRequest request,
final IClientConfig requestConfig) throws ClientException {
throw exception;
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(final ClientRequest clientRequest, final IClientConfig iClientConfig) {
return null;
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
final ClientRequest clientRequest, final IClientConfig iClientConfig) {
return null;
}
@Override
public IResponse execute(final ClientRequest clientRequest, final IClientConfig iClientConfig) throws Exception {
return null;
}
}
@Override
public IResponse execute(final ClientRequest clientRequest,
final IClientConfig iClientConfig) throws Exception {
return null;
}
}
public static class TestFallbackProvider implements FallbackProvider {
public static class TestFallbackProvider implements FallbackProvider {
private final ClientHttpResponse response;
private Throwable cause;
private final ClientHttpResponse response;
private Throwable cause;
public TestFallbackProvider(final ClientHttpResponse response) {
this.response = response;
}
public TestFallbackProvider(final ClientHttpResponse response) {
this.response = response;
}
@Override
public ClientHttpResponse fallbackResponse(final Throwable cause) {
this.cause = cause;
return response;
}
@Override
public ClientHttpResponse fallbackResponse(final Throwable cause) {
this.cause = cause;
return response;
}
@Override
public String getRoute() {
return "test-route";
}
@Override
public String getRoute() {
return "test-route";
}
@Override
public ClientHttpResponse fallbackResponse() {
throw new UnsupportedOperationException("fallback without cause is not supported");
}
@Override
public ClientHttpResponse fallbackResponse() {
throw new UnsupportedOperationException(
"fallback without cause is not supported");
}
public Throwable getCause() {
return cause;
}
public Throwable getCause() {
return cause;
}
public static TestFallbackProvider withResponse(final HttpStatus status) {
return new TestFallbackProvider(getClientHttpResponse(status));
}
}
public static TestFallbackProvider withResponse(final HttpStatus status) {
return new TestFallbackProvider(getClientHttpResponse(status));
}
}
public static class TestZuulFallbackProviderWithoutCause implements ZuulFallbackProvider {
public static class TestZuulFallbackProviderWithoutCause
implements ZuulFallbackProvider {
private final ClientHttpResponse response;
private final ClientHttpResponse response;
public TestZuulFallbackProviderWithoutCause(final ClientHttpResponse response) {
this.response = response;
}
public TestZuulFallbackProviderWithoutCause(final ClientHttpResponse response) {
this.response = response;
}
public TestZuulFallbackProviderWithoutCause(final HttpStatus status) {
this(getClientHttpResponse(status));
}
public TestZuulFallbackProviderWithoutCause(final HttpStatus status) {
this(getClientHttpResponse(status));
}
@Override
public String getRoute() {
return "test-route";
}
@Override
public String getRoute() {
return "test-route";
}
@Override
public ClientHttpResponse fallbackResponse() {
return response;
}
}
@Override
public ClientHttpResponse fallbackResponse() {
return response;
}
}
private static ClientHttpResponse getClientHttpResponse(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
private static ClientHttpResponse getClientHttpResponse(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return getStatusCode().value();
}
@Override
public int getRawStatusCode() throws IOException {
return getStatusCode().value();
}
@Override
public String getStatusText() throws IOException {
return getStatusCode().getReasonPhrase();
}
@Override
public String getStatusText() throws IOException {
return getStatusCode().getReasonPhrase();
}
@Override
public void close() {
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("test".getBytes());
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("test".getBytes());
}
@Override
public HttpHeaders getHeaders() {
return new HttpHeaders();
}
};
}
@Override
public HttpHeaders getHeaders() {
return new HttpHeaders();
}
};
}
}

View File

@@ -20,8 +20,12 @@ import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import static org.assertj.core.api.Assertions.assertThat;
@@ -38,25 +42,38 @@ public class RibbonCommandHystrixThreadPoolKeyTests {
zuulProperties = new ZuulProperties();
}
@After
public void tearDown() throws Exception {
HystrixPlugins.reset();
}
@Test
public void testDefaultHystrixThreadPoolKey() throws Exception {
zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
zuulProperties.setRibbonIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1", zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2", zuulProperties);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1",
zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2",
zuulProperties);
// CommandGroupKey should be used as ThreadPoolKey as default.
assertThat(ribbonCommand1.getThreadPoolKey().name()).isEqualTo(ribbonCommand1.getCommandGroup().name());
assertThat(ribbonCommand2.getThreadPoolKey().name()).isEqualTo(ribbonCommand2.getCommandGroup().name());
assertThat(ribbonCommand1.getThreadPoolKey().name())
.isEqualTo(ribbonCommand1.getCommandGroup().name());
assertThat(ribbonCommand2.getThreadPoolKey().name())
.isEqualTo(ribbonCommand2.getCommandGroup().name());
}
@Test
public void testUseSeparateThreadPools() throws Exception {
zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
zuulProperties.setRibbonIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
zuulProperties.getThreadPool().setUseSeparateThreadPools(true);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1", zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2", zuulProperties);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1",
zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2",
zuulProperties);
assertThat(ribbonCommand1.getThreadPoolKey().name()).isEqualTo("testCommand1");
assertThat(ribbonCommand2.getThreadPoolKey().name()).isEqualTo("testCommand2");
@@ -66,35 +83,45 @@ public class RibbonCommandHystrixThreadPoolKeyTests {
public void testThreadPoolKeyPrefix() throws Exception {
final String prefix = "zuulgw-";
zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
zuulProperties.setRibbonIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
zuulProperties.getThreadPool().setUseSeparateThreadPools(true);
zuulProperties.getThreadPool().setThreadPoolKeyPrefix(prefix);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1", zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2", zuulProperties);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1",
zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2",
zuulProperties);
assertThat(ribbonCommand1.getThreadPoolKey().name()).isEqualTo(prefix + "testCommand1");
assertThat(ribbonCommand2.getThreadPoolKey().name()).isEqualTo(prefix + "testCommand2");
assertThat(ribbonCommand1.getThreadPoolKey().name())
.isEqualTo(prefix + "testCommand1");
assertThat(ribbonCommand2.getThreadPoolKey().name())
.isEqualTo(prefix + "testCommand2");
}
@Test
public void testNoSideEffectOnSemaphoreIsolation() throws Exception {
final String prefix = "zuulgw-";
zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE);
zuulProperties.setRibbonIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE);
zuulProperties.getThreadPool().setUseSeparateThreadPools(true);
zuulProperties.getThreadPool().setThreadPoolKeyPrefix(prefix);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1", zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2", zuulProperties);
TestRibbonCommand ribbonCommand1 = new TestRibbonCommand("testCommand1",
zuulProperties);
TestRibbonCommand ribbonCommand2 = new TestRibbonCommand("testCommand2",
zuulProperties);
// There should be no side effect on semaphore isolation
assertThat(ribbonCommand1.getThreadPoolKey().name()).isEqualTo(ribbonCommand1.getCommandGroup().name());
assertThat(ribbonCommand2.getThreadPoolKey().name()).isEqualTo(ribbonCommand2.getCommandGroup().name());
assertThat(ribbonCommand1.getThreadPoolKey().name())
.isEqualTo(ribbonCommand1.getCommandGroup().name());
assertThat(ribbonCommand2.getThreadPoolKey().name())
.isEqualTo(ribbonCommand2.getCommandGroup().name());
}
public static class TestRibbonCommand
extends AbstractRibbonCommand<AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse>, ClientRequest, HttpResponse> {
public static class TestRibbonCommand extends
AbstractRibbonCommand<AbstractLoadBalancerAwareClient<ClientRequest, HttpResponse>, ClientRequest, HttpResponse> {
public TestRibbonCommand(String commandKey, ZuulProperties zuulProperties) {
super(commandKey, null, null, zuulProperties);
}

View File

@@ -23,8 +23,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Convenience annotation for clients to enable Eureka discovery configuration
* (specifically). Use this (optionally) in case you want discovery and know for sure that
@@ -39,7 +37,6 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EnableDiscoveryClient
public @interface EnableEurekaClient {
}

View File

@@ -73,8 +73,6 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
/**
* @author Dave Syer
* @author Spencer Gibb
@@ -91,7 +89,9 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration")
@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
public class EurekaClientAutoConfiguration {
private static final Log log = LogFactory.getLog(EurekaClientAutoConfiguration.class);

View File

@@ -2,10 +2,9 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

View File

@@ -27,7 +27,6 @@ import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
@@ -62,7 +61,6 @@ public class EurekaHealthCheckTests {
@Configuration
@EnableAutoConfiguration
@EnableEurekaClient
protected static class EurekaHealthCheckApplication {
@Bean

View File

@@ -25,7 +25,6 @@ import org.springframework.boot.actuate.metrics.repository.InMemoryMetricReposit
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
@@ -48,7 +47,6 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
@ComponentScan
@EnableAutoConfiguration
@RestController
@EnableDiscoveryClient
public class EurekaSampleApplication implements ApplicationContextAware, Closeable {
@Autowired

View File

@@ -19,7 +19,6 @@
package org.springframework.cloud.netflix.eureka.sample;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -37,7 +36,6 @@ import static org.mockito.Mockito.mock;
@ComponentScan
@EnableAutoConfiguration
@RestController
@EnableDiscoveryClient
public class RefreshEurekaSampleApplication {
@Bean

View File

@@ -27,7 +27,6 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.test.annotation.DirtiesContext;
@@ -56,7 +55,6 @@ public class RibbonEurekaAutoConfigurationTests {
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableDiscoveryClient
public static class EurekaClientDisabledApp {
@Bean

View File

@@ -22,7 +22,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Import;
/**
@@ -33,7 +32,6 @@ import org.springframework.context.annotation.Import;
*
*/
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented

View File

@@ -23,7 +23,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Import;
@@ -31,7 +30,6 @@ import org.springframework.context.annotation.Import;
* @author Spencer Gibb
*/
@EnableCircuitBreaker
@EnableDiscoveryClient
@EnableZuulProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -30,7 +29,6 @@ import org.springframework.web.bind.annotation.RestController;
* @author Spencer Gibb
*/
@EnableAutoConfiguration
@EnableDiscoveryClient
@Configuration
@RestController
public class SidecarClientApplication {

View File

@@ -23,7 +23,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -36,7 +35,6 @@ import com.netflix.turbine.streaming.servlet.TurbineStreamServlet;
*/
@Configuration
@EnableConfigurationProperties
@EnableDiscoveryClient
public class TurbineHttpConfiguration {
@Bean