mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-17 23:59:04 +00:00
Compare commits
61
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d9363b32f | ||
|
|
08bb746c70 | ||
|
|
661faa8d88 | ||
|
|
7596c1d152 | ||
|
|
3016992fb0 | ||
|
|
bd3391ae55 | ||
|
|
f7cf155eef | ||
|
|
a2ad006899 | ||
|
|
f0fc9df67d | ||
|
|
1270ba0edb | ||
|
|
ac0539cdc4 | ||
|
|
880a6ce4b5 | ||
|
|
b5a0274b58 | ||
|
|
7e6c7ee221 | ||
|
|
f1fb32cfdb | ||
|
|
9bc8bc6bbe | ||
|
|
5073cd89ad | ||
|
|
08ba0cc72b | ||
|
|
ee953bef07 | ||
|
|
4ca231aff9 | ||
|
|
4ee2055d5c | ||
|
|
1fffc923e1 | ||
|
|
b41027191e | ||
|
|
0df5957f6b | ||
|
|
234c4c65ae | ||
|
|
c5117a8573 | ||
|
|
f304ab7101 | ||
|
|
f591136d80 | ||
|
|
9170365b85 | ||
|
|
f5c6920843 | ||
|
|
470d6abf63 | ||
|
|
f3275c160b | ||
|
|
5cd39df5e7 | ||
|
|
a91815aa43 | ||
|
|
0f7f88f89b | ||
|
|
2487ef2fad | ||
|
|
b5195b7b86 | ||
|
|
845590e46c | ||
|
|
7fdd86e69c | ||
|
|
d1f8c03110 | ||
|
|
728ef05bf3 | ||
|
|
27a2c31241 | ||
|
|
3034c55b13 | ||
|
|
cdaa97c006 | ||
|
|
f563fcb4cd | ||
|
|
14dd2d2359 | ||
|
|
8b4469db31 | ||
|
|
658f29ed27 | ||
|
|
19556dc25d | ||
|
|
b31f6c5c4f | ||
|
|
dfcc319ba7 | ||
|
|
fad2c5d400 | ||
|
|
3201234ee1 | ||
|
|
9b64d0da8f | ||
|
|
be8eece91e | ||
|
|
7c48814234 | ||
|
|
72ccf2e649 | ||
|
|
3df42159fb | ||
|
|
45e7a088bb | ||
|
|
0f0c6b6204 | ||
|
|
88a36b38e9 |
+2
-2
@@ -85,11 +85,11 @@ a modified file in the correct place. Just commit it and push the change.
|
||||
If you don't have an IDE preference we would recommend that you use
|
||||
http://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
http://eclipse.org[Eclipse] when working with the code. We use the
|
||||
http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
|
||||
http://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
||||
should also work without issue as long as they use Maven 3.3.3 or better.
|
||||
|
||||
==== Importing into eclipse with m2eclipse
|
||||
We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
|
||||
We recommend the http://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
||||
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
||||
marketplace".
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -1236,7 +1236,8 @@ To enable it, annotate a Spring Boot main class with
|
||||
service. By convention, a service with the ID "users", will
|
||||
receive requests from the proxy located at `/users` (with the prefix
|
||||
stripped). The proxy uses Ribbon to locate an instance to forward to
|
||||
via discovery, and all requests are executed in a hystrix command, so
|
||||
via discovery, and all requests are executed in a
|
||||
<<hystrix-fallbacks-for-routes, hystrix command>>, so
|
||||
failures will show up in Hystrix metrics, and once the circuit is open
|
||||
the proxy will not try to contact the service.
|
||||
|
||||
@@ -1604,6 +1605,70 @@ possible filters that are enabled. If you want to disable one, simply set
|
||||
`org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter` set
|
||||
`zuul.SendResponseFilter.post.disable=true`.
|
||||
|
||||
[[hystrix-fallbacks-for-routes]]
|
||||
=== Providing Hystrix Fallbacks For Routes
|
||||
|
||||
When a circuit for a given route in Zuul is tripped you can provide a fallback response
|
||||
by creating a bean of type `ZuulFallbackProvider`. Within this bean you need to specify
|
||||
the route ID the fallback is for and provide a `ClientHttpResponse` to return
|
||||
as a fallback. Here is a very simple `ZuulFallbackProvider` implementation.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
class MyFallbackProvider implements ZuulFallbackProvider {
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "customers";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse fallbackResponse() {
|
||||
return new ClientHttpResponse() {
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return HttpStatus.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return new ByteArrayInputStream("fallback".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return headers;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
And here is what the route configuration would look like.
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
routes:
|
||||
customers: /customers/**
|
||||
----
|
||||
|
||||
=== Polyglot support with Sidecar
|
||||
|
||||
Do you have non-jvm languages you want to take advantage of Eureka, Ribbon and
|
||||
@@ -1963,3 +2028,28 @@ TIP: After executing several requests against your service, you can gather some
|
||||
The Atlas wiki contains a link:https://github.com/Netflix/atlas/wiki/Single-Line[compilation of sample queries] for various scenarios.
|
||||
|
||||
Make sure to check out the link:https://github.com/Netflix/atlas/wiki/Alerting-Philosophy[alerting philosophy] and docs on using link:https://github.com/Netflix/atlas/wiki/DES[double exponential smoothing] to generate dynamic alert thresholds.
|
||||
|
||||
[[retrying-failed-requests]]
|
||||
=== Retrying Failed Requests
|
||||
|
||||
Spring Cloud Netflix offers a variety of ways to make HTTP requests. You can use a load balanced
|
||||
`RestTemplate`, Ribbon, or Feign. No matter how you choose to your HTTP requests, there is always
|
||||
a chance the request may fail. When a request fails you may want to have the request retried
|
||||
automatically. To accomplish this when using Sping Cloud Netflix you need to include
|
||||
https://github.com/spring-projects/spring-retry[Spring Retry] on your application's classpath.
|
||||
When Spring Retry is present load balanced `RestTemplates`, Feign, and Zuul will automatically
|
||||
retry any failed requests (assuming you configuration allows it to).
|
||||
|
||||
==== Configuration
|
||||
|
||||
Anytime Ribbon is used with Spring Retry you can control the retry functionality by configuring
|
||||
certain Ribbon properties. The properties you can use are
|
||||
`client.ribbon.MaxAutoRetries`, `client.ribbon.MaxAutoRetriesNextServer`, and
|
||||
`client.ribbon.OkToRetryOnAllOperations`. See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties[Ribbon documentation]
|
||||
for a description of what there properties do.
|
||||
|
||||
==== Zuul
|
||||
|
||||
You can turn off Zuul's retry functionality by setting `zuul.retryable` to `false`. You
|
||||
can also disable retry functionality on route by route basis by setting
|
||||
`zuul.routes.routename.retryable` to `false`.
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Netflix</name>
|
||||
<description>Spring Cloud Netflix</description>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.2.RELEASE</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -24,9 +24,9 @@
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<netty.version>4.0.27.Final</netty.version>
|
||||
<jackson.version>2.7.3</jackson.version>
|
||||
<spring-cloud-commons.version>1.1.5.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>1.2.2.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Brooklyn.BUILD-SNAPSHOT</spring-cloud-stream.version>
|
||||
<spring-cloud-commons.version>1.1.8.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>1.2.3.RELEASE</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Brooklyn.SR3</spring-cloud-stream.version>
|
||||
|
||||
<!-- Sonar -->
|
||||
<surefire.plugin.version>2.19.1</surefire.plugin.version>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
|
||||
+16
-11
@@ -16,9 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.Retryer;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -35,16 +42,8 @@ import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Venil Noronha
|
||||
@@ -103,11 +102,17 @@ public class FeignClientsConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Retryer feignRetryer() {
|
||||
return Retryer.NEVER_RETRY;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
public Feign.Builder feignBuilder() {
|
||||
return Feign.builder();
|
||||
public Feign.Builder feignBuilder(Retryer retryer) {
|
||||
return Feign.builder().retryer(retryer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+21
-1
@@ -18,8 +18,11 @@ package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
@@ -34,11 +37,27 @@ import com.netflix.loadbalancer.ILoadBalancer;
|
||||
public class CachingSpringLoadBalancerFactory {
|
||||
|
||||
private final SpringClientFactory factory;
|
||||
private final LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
|
||||
private boolean enableRetry = false;
|
||||
|
||||
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
this.loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(factory);
|
||||
}
|
||||
|
||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
this.factory = factory;
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
}
|
||||
|
||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory, boolean enableRetry) {
|
||||
this.factory = factory;
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
this.enableRetry = enableRetry;
|
||||
}
|
||||
|
||||
public FeignLoadBalancer create(String clientName) {
|
||||
@@ -48,7 +67,8 @@ public class CachingSpringLoadBalancerFactory {
|
||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
|
||||
FeignLoadBalancer client = new FeignLoadBalancer(lb, config, serverIntrospector);
|
||||
FeignLoadBalancer client = enableRetry ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
|
||||
loadBalancedRetryPolicyFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
|
||||
this.cache.put(clientName, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
+45
-15
@@ -16,14 +16,23 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import feign.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.ClientRequest;
|
||||
@@ -35,23 +44,18 @@ import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import feign.Util;
|
||||
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
|
||||
|
||||
public class FeignLoadBalancer extends
|
||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
||||
|
||||
private final int connectTimeout;
|
||||
private final int readTimeout;
|
||||
private final IClientConfig clientConfig;
|
||||
private final ServerIntrospector serverIntrospector;
|
||||
protected int connectTimeout;
|
||||
protected int readTimeout;
|
||||
protected IClientConfig clientConfig;
|
||||
protected ServerIntrospector serverIntrospector;
|
||||
|
||||
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
||||
ServerIntrospector serverIntrospector) {
|
||||
ServerIntrospector serverIntrospector) {
|
||||
super(lb, clientConfig);
|
||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
||||
this.clientConfig = clientConfig;
|
||||
@@ -116,8 +120,6 @@ public class FeignLoadBalancer extends
|
||||
private Request toRequest(Request request) {
|
||||
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
||||
request.headers());
|
||||
// Apache client barfs if you set the content length
|
||||
headers.remove(Util.CONTENT_LENGTH);
|
||||
return Request.create(request.method(),getUri().toASCIIString(),headers,request.body(),request.charset());
|
||||
}
|
||||
|
||||
@@ -129,6 +131,34 @@ public class FeignLoadBalancer extends
|
||||
return this.client;
|
||||
}
|
||||
|
||||
HttpRequest toHttpRequest() {
|
||||
return new HttpRequest() {
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.resolve(RibbonRequest.this.toRequest().method());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return RibbonRequest.this.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
Map<String, List<String>> headers = new HashMap<String, List<String>>();
|
||||
Map<String, Collection<String>> feignHeaders = RibbonRequest.this.toRequest().headers();
|
||||
for(String key : feignHeaders.keySet()) {
|
||||
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
|
||||
}
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(headers);
|
||||
return httpHeaders;
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
return new RibbonRequest(this.client, this.request, getUri());
|
||||
@@ -183,4 +213,4 @@ public class FeignLoadBalancer extends
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 the original author or authors.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryContext;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
||||
private HttpRequest request;
|
||||
private String serviceId;
|
||||
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
|
||||
super(request, policy, serviceInstanceChooser, serviceName);
|
||||
this.request = request;
|
||||
this.serviceId = serviceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRetry(RetryContext context) {
|
||||
/*
|
||||
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server if one is not
|
||||
* set in the retry context and then return true. RetryTemplat calls the canRetry method of
|
||||
* the policy even on its first execution. So the fact that we didnt have a service instance set
|
||||
* in the RetryContext signaled that it was the first execution and we should return true.
|
||||
*
|
||||
* In the Feign scenario, Feign as actually already queried the load balancer for a service instance
|
||||
* and we set that service instance in the context when we call the open method of the policy. So in
|
||||
* the Feign case we just return true if the retry count is 0 indicating we haven't yet made a failed
|
||||
* request.
|
||||
*/
|
||||
if(context.getRetryCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return super.canRetry(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetryContext open(RetryContext parent) {
|
||||
/*
|
||||
* With Feign (unlike Ribbon) the request already has the URI for the service instance
|
||||
* we are going to make the request to, so extract that information and set the service
|
||||
* instance in the context. In the Ribbon scenario the URI in the request object still has
|
||||
* the service id so we choose and set the service instance later on.
|
||||
*/
|
||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent, this.request);
|
||||
context.setServiceInstance(new FeignRetryPolicyServiceInstance(serviceId, request));
|
||||
return context;
|
||||
}
|
||||
|
||||
class FeignRetryPolicyServiceInstance implements ServiceInstance {
|
||||
|
||||
private String serviceId;
|
||||
private HttpRequest request;
|
||||
private Map<String, String> metadata;
|
||||
|
||||
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
|
||||
this.serviceId = serviceId;
|
||||
this.request = request;
|
||||
this.metadata = new HashMap<String, String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return request.getURI().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return request.getURI().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return "https".equals(request.getURI().getScheme());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -21,7 +21,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -49,11 +51,20 @@ public class FeignRibbonClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
|
||||
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
|
||||
SpringClientFactory factory) {
|
||||
return new CachingSpringLoadBalancerFactory(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
|
||||
SpringClientFactory factory, LoadBalancedRetryPolicyFactory retryPolicyFactory) {
|
||||
return new CachingSpringLoadBalancerFactory(factory, retryPolicyFactory, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 the original author or authors.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
/**
|
||||
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements ServiceInstanceChooser {
|
||||
|
||||
private final LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
|
||||
|
||||
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
||||
ServerIntrospector serverIntrospector, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
super(lb, clientConfig, serverIntrospector);
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonResponse execute(final RibbonRequest request, IClientConfig configOverride)
|
||||
throws IOException {
|
||||
final Request.Options options;
|
||||
if (configOverride != null) {
|
||||
options = new Request.Options(
|
||||
configOverride.get(CommonClientConfigKey.ConnectTimeout,
|
||||
this.connectTimeout),
|
||||
(configOverride.get(CommonClientConfigKey.ReadTimeout,
|
||||
this.readTimeout)));
|
||||
}
|
||||
else {
|
||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||
}
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
|
||||
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName()));
|
||||
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
|
||||
@Override
|
||||
public RibbonResponse doWithRetry(RetryContext retryContext) throws IOException {
|
||||
Request feignRequest = null;
|
||||
//on retries the policy will choose the server and set it in the context
|
||||
//extract the server and update the request being made
|
||||
if(retryContext instanceof LoadBalancedRetryContext) {
|
||||
ServiceInstance service = ((LoadBalancedRetryContext)retryContext).getServiceInstance();
|
||||
if(service != null) {
|
||||
feignRequest = ((RibbonRequest)request.replaceUri(reconstructURIWithServer(new Server(service.getHost(), service.getPort()), request.getUri()))).toRequest();
|
||||
}
|
||||
}
|
||||
if(feignRequest == null) {
|
||||
feignRequest = request.toRequest();
|
||||
}
|
||||
Response response = request.client().execute(feignRequest, options);
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
|
||||
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInstance choose(String serviceId) {
|
||||
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
||||
this.getLoadBalancer().chooseServer(serviceId));
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -28,20 +28,18 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.IClient;
|
||||
@@ -55,7 +53,7 @@ import com.netflix.ribbon.Ribbon;
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ IClient.class, RestTemplate.class })
|
||||
@ConditionalOnClass({ IClient.class, RestTemplate.class, Ribbon.class})
|
||||
@RibbonClients
|
||||
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
|
||||
@AutoConfigureBefore(LoadBalancerAutoConfiguration.class)
|
||||
@@ -88,6 +86,12 @@ public class RibbonAutoConfiguration {
|
||||
return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
|
||||
public LoadBalancedRetryPolicyFactory neverRetryPolicyFactory() {
|
||||
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public PropertiesFactory propertiesFactory() {
|
||||
|
||||
+42
-3
@@ -26,10 +26,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
|
||||
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
|
||||
import org.springframework.cloud.netflix.ribbon.okhttp.RetryableOkHttpLoadBalancingClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
@@ -124,6 +128,7 @@ public class RibbonClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
|
||||
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
|
||||
public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
|
||||
IClientConfig config, ServerIntrospector serverIntrospector,
|
||||
ILoadBalancer loadBalancer, RetryHandler retryHandler) {
|
||||
@@ -134,6 +139,21 @@ public class RibbonClientConfiguration {
|
||||
Monitors.registerObject("Client_" + this.name, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
|
||||
IClientConfig config, ServerIntrospector serverIntrospector,
|
||||
ILoadBalancer loadBalancer, RetryHandler retryHandler,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
|
||||
config, serverIntrospector, loadBalancedRetryPolicyFactory);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
client.setRetryHandler(retryHandler);
|
||||
Monitors.registerObject("Client_" + this.name, client);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -143,11 +163,30 @@ public class RibbonClientConfiguration {
|
||||
@Value("${ribbon.client.name}")
|
||||
private String name = "client";
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
|
||||
public OkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config,
|
||||
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
|
||||
RetryHandler retryHandler) {
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
public RetryableOkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config,
|
||||
ServerIntrospector serverIntrospector,
|
||||
ILoadBalancer loadBalancer,
|
||||
RetryHandler retryHandler,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(config,
|
||||
serverIntrospector, loadBalancedRetryPolicyFactory);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
client.setRetryHandler(retryHandler);
|
||||
Monitors.registerObject("Client_" + this.name, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
|
||||
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
|
||||
public OkHttpLoadBalancingClient retryableOkHttpLoadBalancingClient(IClientConfig config,
|
||||
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
|
||||
RetryHandler retryHandler) {
|
||||
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config,
|
||||
serverIntrospector);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
|
||||
+3
-3
@@ -141,17 +141,17 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
return this.clientFactory.getLoadBalancer(serviceId);
|
||||
}
|
||||
|
||||
protected static class RibbonServer implements ServiceInstance {
|
||||
public static class RibbonServer implements ServiceInstance {
|
||||
private final String serviceId;
|
||||
private final Server server;
|
||||
private final boolean secure;
|
||||
private Map<String, String> metadata;
|
||||
|
||||
protected RibbonServer(String serviceId, Server server) {
|
||||
public RibbonServer(String serviceId, Server server) {
|
||||
this(serviceId, server, false, Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
protected RibbonServer(String serviceId, Server server, boolean secure,
|
||||
public RibbonServer(String serviceId, Server server, boolean secure,
|
||||
Map<String, String> metadata) {
|
||||
this.serviceId = serviceId;
|
||||
this.server = server;
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ public class RibbonUtils {
|
||||
public static URI updateToHttpsIfNeeded(URI uri, IClientConfig config, ServerIntrospector serverIntrospector,
|
||||
Server server) {
|
||||
String scheme = uri.getScheme();
|
||||
if (!"https".equals(scheme) && isSecure(config, serverIntrospector, server)) {
|
||||
if (!"".equals(uri.toString()) && !"https".equals(scheme) && isSecure(config, serverIntrospector, server)) {
|
||||
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(uri).scheme("https");
|
||||
if (uri.getRawQuery() != null) {
|
||||
// When building the URI, UriComponentsBuilder verify the allowed characters and does not
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
*
|
||||
* * 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.apache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
/**
|
||||
* An Apache HTTP client which leverages Spring Retry to retry failed requests.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient implements ServiceInstanceChooser {
|
||||
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory =
|
||||
new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
|
||||
public RetryableRibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
super(config, serverIntrospector);
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
|
||||
final RequestConfig.Builder builder = RequestConfig.custom();
|
||||
IClientConfig config = configOverride != null ? configOverride : this.config;
|
||||
builder.setConnectTimeout(config.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
|
||||
builder.setSocketTimeout(config.get(
|
||||
CommonClientConfigKey.ReadTimeout, this.readTimeout));
|
||||
builder.setRedirectsEnabled(config.get(
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
|
||||
final RequestConfig requestConfig = builder.build();
|
||||
return this.executeWithRetry(request, new RetryCallback() {
|
||||
@Override
|
||||
public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
|
||||
//on retries the policy will choose the server and set it in the context
|
||||
//extract the server and update the request being made
|
||||
RibbonApacheHttpRequest newRequest = request;
|
||||
if(context instanceof LoadBalancedRetryContext) {
|
||||
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
|
||||
if(service != null) {
|
||||
//Reconstruct the request URI using the host and port set in the retry context
|
||||
newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
|
||||
newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
|
||||
newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
|
||||
newRequest.getURI().getFragment()));
|
||||
}
|
||||
}
|
||||
if (isSecure(configOverride)) {
|
||||
final URI secureUri = UriComponentsBuilder.fromUri(newRequest.getUri())
|
||||
.scheme("https").build().toUri();
|
||||
newRequest = newRequest.withNewUri(secureUri);
|
||||
}
|
||||
HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
|
||||
final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
|
||||
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
|
||||
LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
boolean retryable = request.getContext() == null ? true :
|
||||
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
|
||||
retryTemplate.setRetryPolicy(retryPolicy == null || !retryable ? new NeverRetryPolicy()
|
||||
: new RetryPolicy(request, retryPolicy, this, this.getClientName()));
|
||||
return retryTemplate.execute(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInstance choose(String serviceId) {
|
||||
Server server = this.getLoadBalancer().chooseServer(serviceId);
|
||||
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
||||
server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
|
||||
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
|
||||
}
|
||||
|
||||
static class RetryPolicy extends FeignRetryPolicy {
|
||||
public RetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
|
||||
super(request, policy, serviceInstanceChooser, serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-17
@@ -27,6 +27,8 @@ import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
@@ -36,10 +38,10 @@ 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
|
||||
public class RibbonLoadBalancingHttpClient extends
|
||||
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> {
|
||||
|
||||
@Deprecated
|
||||
@@ -75,28 +77,20 @@ public class RibbonLoadBalancingHttpClient
|
||||
public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
final RequestConfig.Builder builder = RequestConfig.custom();
|
||||
if (configOverride != null) {
|
||||
builder.setConnectTimeout(configOverride.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
|
||||
builder.setSocketTimeout(configOverride.get(
|
||||
CommonClientConfigKey.ReadTimeout, this.readTimeout));
|
||||
builder.setRedirectsEnabled(configOverride.get(
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
}
|
||||
else {
|
||||
builder.setConnectTimeout(this.connectTimeout);
|
||||
builder.setSocketTimeout(this.readTimeout);
|
||||
builder.setRedirectsEnabled(this.followRedirects);
|
||||
}
|
||||
IClientConfig config = configOverride != null ? configOverride : this.config;
|
||||
builder.setConnectTimeout(config.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
|
||||
builder.setSocketTimeout(config.get(
|
||||
CommonClientConfigKey.ReadTimeout, this.readTimeout));
|
||||
builder.setRedirectsEnabled(config.get(
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
|
||||
final RequestConfig requestConfig = builder.build();
|
||||
|
||||
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());
|
||||
@@ -108,4 +102,8 @@ public class RibbonLoadBalancingHttpClient
|
||||
return super.reconstructURIWithServer(server, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
|
||||
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-20
@@ -36,6 +36,7 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class OkHttpLoadBalancingClient
|
||||
extends AbstractLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {
|
||||
@@ -69,7 +70,6 @@ public class OkHttpLoadBalancingClient
|
||||
public OkHttpRibbonResponse execute(OkHttpRibbonRequest ribbonRequest,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
boolean secure = isSecure(configOverride);
|
||||
|
||||
if (secure) {
|
||||
final URI secureUri = UriComponentsBuilder.fromUri(ribbonRequest.getUri())
|
||||
.scheme("https").build().toUri();
|
||||
@@ -77,7 +77,6 @@ public class OkHttpLoadBalancingClient
|
||||
}
|
||||
|
||||
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
|
||||
|
||||
final Request request = ribbonRequest.toRequest();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
return new OkHttpRibbonResponse(response, ribbonRequest.getUri());
|
||||
@@ -85,25 +84,16 @@ public class OkHttpLoadBalancingClient
|
||||
|
||||
OkHttpClient getOkHttpClient(IClientConfig configOverride, boolean secure) {
|
||||
OkHttpClient.Builder builder = this.delegate.newBuilder();
|
||||
if (configOverride != null) {
|
||||
builder.connectTimeout(configOverride.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout), TimeUnit.MILLISECONDS);
|
||||
builder.readTimeout(configOverride.get(
|
||||
CommonClientConfigKey.ReadTimeout, this.readTimeout), TimeUnit.MILLISECONDS);
|
||||
builder.followRedirects(configOverride.get(
|
||||
IClientConfig config = configOverride != null ? configOverride : this.config;
|
||||
builder.connectTimeout(config.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout), TimeUnit.MILLISECONDS);
|
||||
builder.readTimeout(config.get(
|
||||
CommonClientConfigKey.ReadTimeout, this.readTimeout), TimeUnit.MILLISECONDS);
|
||||
builder.followRedirects(config.get(
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
if (secure) {
|
||||
builder.followSslRedirects(configOverride.get(
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
if (secure) {
|
||||
builder.followSslRedirects(configOverride.get(
|
||||
CommonClientConfigKey.FollowRedirects, this.followRedirects));
|
||||
}
|
||||
}
|
||||
else {
|
||||
builder.connectTimeout(this.connectTimeout, TimeUnit.MILLISECONDS);
|
||||
builder.readTimeout(this.readTimeout, TimeUnit.MILLISECONDS);
|
||||
builder.followRedirects(this.followRedirects);
|
||||
if (secure) {
|
||||
builder.followSslRedirects(this.followRedirects);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
*
|
||||
* * 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.okhttp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
/**
|
||||
* An OK HTTP client which leverages Spring Retry to retry failed request.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClient implements ServiceInstanceChooser {
|
||||
|
||||
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
|
||||
|
||||
public RetryableOkHttpLoadBalancingClient(IClientConfig config, ServerIntrospector serverIntrospector,
|
||||
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
|
||||
super(config, serverIntrospector);
|
||||
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
|
||||
}
|
||||
|
||||
private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request,
|
||||
RetryCallback<OkHttpRibbonResponse, 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 OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest,
|
||||
final IClientConfig configOverride) throws Exception {
|
||||
return this.executeWithRetry(ribbonRequest, new RetryCallback() {
|
||||
@Override
|
||||
public OkHttpRibbonResponse doWithRetry(RetryContext context) throws Exception {
|
||||
//on retries the policy will choose the server and set it in the context
|
||||
//extract the server and update the request being made
|
||||
OkHttpRibbonRequest newRequest = ribbonRequest;
|
||||
if(context instanceof LoadBalancedRetryContext) {
|
||||
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
|
||||
if(service != null) {
|
||||
//Reconstruct the request URI using the host and port set in the retry context
|
||||
newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
|
||||
newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
|
||||
newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
|
||||
newRequest.getURI().getFragment()));
|
||||
}
|
||||
}
|
||||
if (isSecure(configOverride)) {
|
||||
final URI secureUri = UriComponentsBuilder.fromUri(newRequest.getUri())
|
||||
.scheme("https").build().toUri();
|
||||
newRequest = newRequest.withNewUri(secureUri);
|
||||
}
|
||||
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
|
||||
|
||||
final Request request = newRequest.toRequest();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
return new OkHttpRibbonResponse(response, newRequest.getUri());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInstance choose(String serviceId) {
|
||||
Server server = this.getLoadBalancer().chooseServer(serviceId);
|
||||
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
||||
server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(OkHttpRibbonRequest request, IClientConfig requestConfig) {
|
||||
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
|
||||
}
|
||||
|
||||
static class RetryPolicy extends FeignRetryPolicy {
|
||||
public RetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
|
||||
super(request, policy, serviceInstanceChooser, serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -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(),
|
||||
|
||||
+4
-7
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -24,19 +28,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import static com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE;
|
||||
|
||||
/**
|
||||
|
||||
+41
-20
@@ -16,17 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters.post;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.config.DynamicBooleanProperty;
|
||||
import com.netflix.config.DynamicIntProperty;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
@@ -37,8 +36,6 @@ import com.netflix.zuul.constants.ZuulHeaders;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.util.HTTPRequestUtils;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -51,12 +48,30 @@ public class SendResponseFilter extends ZuulFilter {
|
||||
|
||||
private static DynamicIntProperty INITIAL_STREAM_BUFFER_SIZE = DynamicPropertyFactory
|
||||
.getInstance()
|
||||
.getIntProperty(ZuulConstants.ZUUL_INITIAL_STREAM_BUFFER_SIZE, 1024);
|
||||
.getIntProperty(ZuulConstants.ZUUL_INITIAL_STREAM_BUFFER_SIZE, 8192);
|
||||
|
||||
private static DynamicBooleanProperty SET_CONTENT_LENGTH = DynamicPropertyFactory
|
||||
.getInstance()
|
||||
.getBooleanProperty(ZuulConstants.ZUUL_SET_CONTENT_LENGTH, false);
|
||||
private boolean useServlet31 = true;
|
||||
|
||||
public SendResponseFilter() {
|
||||
super();
|
||||
// To support Servlet API 3.0.1 we need to check if setcontentLengthLong exists
|
||||
try {
|
||||
HttpServletResponse.class.getMethod("setContentLengthLong");
|
||||
} catch(NoSuchMethodException e) {
|
||||
useServlet31 = false;
|
||||
}
|
||||
}
|
||||
|
||||
private ThreadLocal<byte[]> buffers = new ThreadLocal<byte[]>() {
|
||||
@Override
|
||||
protected byte[] initialValue() {
|
||||
return new byte[INITIAL_STREAM_BUFFER_SIZE.get()];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String filterType() {
|
||||
return "post";
|
||||
@@ -167,21 +182,16 @@ public class SendResponseFilter extends ZuulFilter {
|
||||
}
|
||||
|
||||
private void writeResponse(InputStream zin, OutputStream out) throws Exception {
|
||||
byte[] bytes = new byte[INITIAL_STREAM_BUFFER_SIZE.get()];
|
||||
int bytesRead = -1;
|
||||
while ((bytesRead = zin.read(bytes)) != -1) {
|
||||
try {
|
||||
try {
|
||||
byte[] bytes = buffers.get();
|
||||
int bytesRead = -1;
|
||||
while ((bytesRead = zin.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, bytesRead);
|
||||
out.flush();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
// doubles buffer size if previous read filled it
|
||||
if (bytesRead == bytes.length) {
|
||||
bytes = new byte[bytes.length * 2];
|
||||
}
|
||||
}
|
||||
catch(IOException ioe) {
|
||||
log.warn("Error while sending response to client: "+ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void addResponseHeaders() {
|
||||
@@ -210,10 +220,21 @@ public class SendResponseFilter extends ZuulFilter {
|
||||
// Only inserts Content-Length if origin provides it and origin response is not
|
||||
// gzipped
|
||||
if (SET_CONTENT_LENGTH.get()) {
|
||||
if (contentLength != null && !ctx.getResponseGZipped()) {
|
||||
servletResponse.setContentLengthLong(contentLength);
|
||||
if ( contentLength != null && !ctx.getResponseGZipped()) {
|
||||
if(useServlet31) {
|
||||
servletResponse.setContentLengthLong(contentLength);
|
||||
} else {
|
||||
//Try and set some kind of content length if we can safely convert the Long to an int
|
||||
if (isLongSafe(contentLength)) {
|
||||
servletResponse.setContentLength(contentLength.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLongSafe(long value) {
|
||||
return value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-1
@@ -170,7 +170,6 @@ public class FormBodyWrapperFilter extends ZuulFilter {
|
||||
return this.contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthLong() {
|
||||
return getContentLength();
|
||||
}
|
||||
|
||||
+12
-6
@@ -16,28 +16,25 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters.route;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
@CommonsLog
|
||||
public class RibbonRoutingFilter extends ZuulFilter {
|
||||
|
||||
@@ -45,6 +42,7 @@ public class RibbonRoutingFilter extends ZuulFilter {
|
||||
protected ProxyRequestHelper helper;
|
||||
protected RibbonCommandFactory<?> ribbonCommandFactory;
|
||||
protected List<RibbonRequestCustomizer> requestCustomizers;
|
||||
private boolean useServlet31 = true;
|
||||
|
||||
public RibbonRoutingFilter(ProxyRequestHelper helper,
|
||||
RibbonCommandFactory<?> ribbonCommandFactory,
|
||||
@@ -52,6 +50,12 @@ public class RibbonRoutingFilter extends ZuulFilter {
|
||||
this.helper = helper;
|
||||
this.ribbonCommandFactory = ribbonCommandFactory;
|
||||
this.requestCustomizers = requestCustomizers;
|
||||
// To support Servlet API 3.0.1 we need to check if getcontentLengthLong exists
|
||||
try {
|
||||
HttpServletResponse.class.getMethod("getContentLengthLong");
|
||||
} catch(NoSuchMethodException e) {
|
||||
useServlet31 = false;
|
||||
}
|
||||
}
|
||||
|
||||
public RibbonRoutingFilter(RibbonCommandFactory<?> ribbonCommandFactory) {
|
||||
@@ -119,8 +123,10 @@ public class RibbonRoutingFilter extends ZuulFilter {
|
||||
// remove double slashes
|
||||
uri = uri.replace("//", "/");
|
||||
|
||||
long contentLength = useServlet31 ? request.getContentLengthLong(): request.getContentLength();
|
||||
|
||||
return new RibbonCommandContext(serviceId, verb, uri, retryable, headers, params,
|
||||
requestEntity, this.requestCustomizers, request.getContentLengthLong());
|
||||
requestEntity, this.requestCustomizers, contentLength);
|
||||
}
|
||||
|
||||
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
|
||||
|
||||
+9
-3
@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.zuul.filters.route;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
@@ -273,9 +274,14 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
HttpHost httpHost = getHttpHost(host);
|
||||
uri = StringUtils.cleanPath((host.getPath() + uri).replaceAll("/{2,}", "/"));
|
||||
int contentLength = request.getContentLength();
|
||||
InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength,
|
||||
request.getContentType() != null
|
||||
? ContentType.create(request.getContentType()) : null);
|
||||
|
||||
ContentType contentType = null;
|
||||
|
||||
if (request.getContentType() != null) {
|
||||
contentType = ContentType.parse(request.getContentType());
|
||||
}
|
||||
|
||||
InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength, contentType);
|
||||
|
||||
HttpRequest httpRequest = buildHttpRequest(verb, uri, entity, headers, params);
|
||||
try {
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ public class FeignClientOverrideDefaultsTests {
|
||||
|
||||
@Test
|
||||
public void overrideRetryer() {
|
||||
assertNull(this.context.getInstance("foo", Retryer.class));
|
||||
assertEquals(Retryer.NEVER_RETRY, this.context.getInstance("foo", Retryer.class));
|
||||
Retryer.Default.class.cast(this.context.getInstance("bar", Retryer.class));
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -16,21 +16,23 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -39,6 +41,9 @@ public class CachingSpringLoadBalancerFactoryTests {
|
||||
@Mock
|
||||
private SpringClientFactory delegate;
|
||||
|
||||
@Mock
|
||||
private RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
|
||||
|
||||
private CachingSpringLoadBalancerFactory factory;
|
||||
|
||||
@Before
|
||||
@@ -52,7 +57,7 @@ public class CachingSpringLoadBalancerFactoryTests {
|
||||
when(this.delegate.getClientConfig("client1")).thenReturn(config);
|
||||
when(this.delegate.getClientConfig("client2")).thenReturn(config);
|
||||
|
||||
this.factory = new CachingSpringLoadBalancerFactory(this.delegate);
|
||||
this.factory = new CachingSpringLoadBalancerFactory(this.delegate, loadBalancedRetryPolicyFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+25
-27
@@ -1,5 +1,30 @@
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonResponse;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.IsSecure;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
|
||||
@@ -15,33 +40,6 @@ import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonResponse;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class FeignLoadBalancerTests {
|
||||
|
||||
@Mock
|
||||
|
||||
+8
-9
@@ -16,14 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -43,13 +42,12 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
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 lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests the Feign Retryer, not ribbon retry.
|
||||
@@ -58,7 +56,8 @@ import lombok.NoArgsConstructor;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
"spring.application.name=feignclientretrytest", "feign.okhttp.enabled=false",
|
||||
"feign.httpclient.enabled=false", "feign.hystrix.enabled=false", })
|
||||
"feign.httpclient.enabled=false", "feign.hystrix.enabled=false", "localapp.ribbon.MaxAutoRetries=2",
|
||||
"localapp.ribbon.MaxAutoRetriesNextServer=3"})
|
||||
@DirtiesContext
|
||||
public class FeignRibbonClientRetryTests {
|
||||
|
||||
|
||||
+14
-11
@@ -16,19 +16,19 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import org.hamcrest.CustomMatcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
@@ -38,10 +38,11 @@ import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -51,6 +52,7 @@ public class FeignRibbonClientTests {
|
||||
|
||||
private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class);
|
||||
private Client delegate = mock(Client.class);
|
||||
private RibbonLoadBalancedRetryPolicyFactory retryPolicyFactory = mock(RibbonLoadBalancedRetryPolicyFactory.class);
|
||||
|
||||
private SpringClientFactory factory = new SpringClientFactory() {
|
||||
@Override
|
||||
@@ -79,7 +81,8 @@ public class FeignRibbonClientTests {
|
||||
|
||||
// Even though we don't maintain FeignRibbonClient, keep these tests
|
||||
// around to make sure the expected behaviour doesn't break
|
||||
private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory), this.factory);
|
||||
private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory,
|
||||
retryPolicyFactory), this.factory);
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 the original author or authors.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.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.http.HttpRequest;
|
||||
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetriesNextServer;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOperations;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout;
|
||||
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
|
||||
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.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;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RetryableFeignLoadBalancerTest {
|
||||
@Mock
|
||||
private ILoadBalancer lb;
|
||||
@Mock
|
||||
private IClientConfig config;
|
||||
private ServerIntrospector inspector = new DefaultServerIntrospector();
|
||||
|
||||
private Integer defaultConnectTimeout = 10000;
|
||||
private Integer defaultReadTimeout = 10000;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(this.config.get(MaxAutoRetries, DEFAULT_MAX_AUTO_RETRIES)).thenReturn(1);
|
||||
when(this.config.get(MaxAutoRetriesNextServer,
|
||||
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(1);
|
||||
when(this.config.get(OkToRetryOnAllOperations, eq(anyBoolean())))
|
||||
.thenReturn(true);
|
||||
when(this.config.get(ConnectTimeout)).thenReturn(this.defaultConnectTimeout);
|
||||
when(this.config.get(ReadTimeout)).thenReturn(this.defaultReadTimeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeNoFailure() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertEquals(200, ribbonResponse.toResponse().status());
|
||||
verify(client, times(1)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeNeverRetry() throws Exception {
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
doThrow(new IOException("boom")).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, new LoadBalancedRetryPolicyFactory() {
|
||||
@Override
|
||||
public LoadBalancedRetryPolicy create(String s, ServiceInstanceChooser serviceInstanceChooser) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
try {
|
||||
feignLb.execute(request, null);
|
||||
} catch(Exception e) {
|
||||
assertThat(e, instanceOf(IOException.class));
|
||||
} finally {
|
||||
verify(client, times(1)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeRetry() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertEquals(200, ribbonResponse.toResponse().status());
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestSpecificRetryHandler() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
RequestSpecificRetryHandler retryHandler = feignLb.getRequestSpecificRetryHandler(request, config);
|
||||
assertEquals(1, retryHandler.getMaxRetriesOnNextServer());
|
||||
assertEquals(1, retryHandler.getMaxRetriesOnSameServer());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void choose() throws Exception {
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
final Server server = new Server("foo", 80);
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(new ILoadBalancer() {
|
||||
@Override
|
||||
public void addServers(List<Server> list) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server chooseServer(Object o) {
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markServerDown(Server server) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Server> getServerList(boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Server> getReachableServers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Server> getAllServers() {
|
||||
return null;
|
||||
}
|
||||
}, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
ServiceInstance serviceInstance = feignLb.choose("foo");
|
||||
assertEquals("foo", serviceInstance.getHost());
|
||||
assertEquals(80, serviceInstance.getPort());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -60,6 +60,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@@ -403,8 +404,11 @@ public class FeignClientTests {
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST, consumes = "application/vnd.io.spring.cloud.test.v1+json", produces = "application/vnd.io.spring.cloud.test.v1+json", path = "/complex")
|
||||
String complex(String body) {
|
||||
return "{\"value\":\"OK\"}";
|
||||
String complex(@RequestBody String body, @RequestHeader("Content-Length") int contentLength) {
|
||||
if (contentLength <= 0) {
|
||||
throw new IllegalArgumentException("Invalid Content-Length "+ contentLength);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/tostring")
|
||||
|
||||
+2
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.netflix.feign.valid;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
@@ -82,6 +83,7 @@ public class FeignClientValidationTests {
|
||||
@Test
|
||||
public void validLoadBalanced() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
LoadBalancerAutoConfiguration.class,
|
||||
RibbonAutoConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class,
|
||||
GoodServiceIdConfiguration.class);
|
||||
|
||||
+10
-1
@@ -42,6 +42,8 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -110,7 +112,14 @@ public class FeignHttpClientTests {
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.PATCH, value = "/hellop")
|
||||
public ResponseEntity<Void> patchHello() {
|
||||
public ResponseEntity<Void> patchHello(@RequestBody Hello hello,
|
||||
@RequestHeader("Content-Length") int contentLength) {
|
||||
if (contentLength <= 0) {
|
||||
throw new IllegalArgumentException("Invalid Content-Length "+ contentLength);
|
||||
}
|
||||
if (!hello.getMessage().equals("foo")) {
|
||||
throw new IllegalArgumentException("Invalid Hello: " + hello.getMessage());
|
||||
}
|
||||
return ResponseEntity.ok().header("X-Hello", "hello world patch").build();
|
||||
}
|
||||
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.feign.valid;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
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.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import feign.Client;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = FeignOkHttpTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
"spring.application.name=feignclienttest", "feign.hystrix.enabled=false",
|
||||
"feign.okhttp.enabled=true" })
|
||||
@DirtiesContext
|
||||
public class FeignOkHttpTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Autowired
|
||||
private TestClient testClient;
|
||||
|
||||
@Autowired
|
||||
private Client feignClient;
|
||||
|
||||
@Autowired
|
||||
private UserClient userClient;
|
||||
|
||||
@FeignClient("localapp")
|
||||
protected interface TestClient extends BaseTestClient {
|
||||
}
|
||||
|
||||
protected interface BaseTestClient {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
Hello getHello();
|
||||
|
||||
@RequestMapping(method = RequestMethod.PATCH, value = "/hellop", consumes = "application/json")
|
||||
ResponseEntity<Void> patchHello(Hello hello);
|
||||
}
|
||||
|
||||
protected interface UserService {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
|
||||
User getUser(@PathVariable("id") long id);
|
||||
}
|
||||
|
||||
@FeignClient("localapp")
|
||||
protected interface UserClient extends UserService {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
protected static class Application implements UserService {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
public Hello getHello() {
|
||||
return new Hello("hello world 1");
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.PATCH, value = "/hellop")
|
||||
public ResponseEntity<Void> patchHello(@RequestBody Hello hello,
|
||||
@RequestHeader("Content-Length") int contentLength) {
|
||||
if (contentLength <= 0) {
|
||||
throw new IllegalArgumentException("Invalid Content-Length "+ contentLength);
|
||||
}
|
||||
if (!hello.getMessage().equals("foo")) {
|
||||
throw new IllegalArgumentException("Invalid Hello: " + hello.getMessage());
|
||||
}
|
||||
return ResponseEntity.ok().header("X-Hello", "hello world patch").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(@PathVariable("id") long id) {
|
||||
return new User("John Smith");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleType() {
|
||||
Hello hello = this.testClient.getHello();
|
||||
assertNotNull("hello was null", hello);
|
||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatch() {
|
||||
ResponseEntity<Void> response = this.testClient.patchHello(new Hello("foo"));
|
||||
assertThat(response, is(notNullValue()));
|
||||
String header = response.getHeaders().getFirst("X-Hello");
|
||||
assertThat(header, equalTo("hello world patch"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class)));
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate, is(instanceOf(feign.okhttp.OkHttpClient.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeignInheritanceSupport() {
|
||||
assertNotNull("UserClient was null", this.userClient);
|
||||
final User user = this.userClient.getUser(1);
|
||||
assertNotNull("Returned user was null", user);
|
||||
assertEquals("Users were different", user, new User("John Smith"));
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Hello {
|
||||
private String message;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class User {
|
||||
private String name;
|
||||
}
|
||||
|
||||
// Load balancer with fixed server list for "local" pointing to localhost
|
||||
@Configuration
|
||||
static class LocalRibbonClientConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2013-2016 the original author or authors.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.ClassPathExclusions;
|
||||
import org.springframework.cloud.FilteredClassPathRunner;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(FilteredClassPathRunner.class)
|
||||
@ClassPathExclusions({"ribbon-{version:\\d.*}.jar"})
|
||||
public class RibbonDisabledTests {
|
||||
|
||||
@Test(expected = ArrayStoreException.class)
|
||||
public void testRibbonDisabled() {
|
||||
new SpringApplicationBuilder().web(false)
|
||||
.sources(RibbonAutoConfiguration.class).run();
|
||||
}
|
||||
}
|
||||
+8
@@ -110,6 +110,14 @@ public class RibbonUtilsTests {
|
||||
"https://foo/%20bar?hello=1%202")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyStringUri() throws URISyntaxException {
|
||||
URI original = new URI("");
|
||||
URI updated = updateToHttpsIfNeeded(original, SECURE_CONFIG, SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("URI should be the emptry string", updated, is(new URI(
|
||||
"")));
|
||||
}
|
||||
|
||||
static DefaultClientConfigImpl getConfig(boolean value) {
|
||||
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
|
||||
config.setProperty(CommonClientConfigKey.IsSecure, value);
|
||||
|
||||
+20
-2
@@ -28,10 +28,18 @@ import org.springframework.cloud.ClassPathExclusions;
|
||||
import org.springframework.cloud.FilteredClassPathRunner;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.RetryableFeignLoadBalancer;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -45,7 +53,8 @@ public class SpringRetryDisabledTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
.sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class).run();
|
||||
.sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class).run();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -58,6 +67,15 @@ public class SpringRetryDisabledTests {
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
|
||||
assertThat(factories.values(), hasSize(0));
|
||||
assertThat(factories.values(), hasSize(1));
|
||||
assertThat(factories.values().toArray()[0], instanceOf(LoadBalancedRetryPolicyFactory.NeverRetryFactory.class));
|
||||
Map<String, RibbonLoadBalancingHttpClient> clients = context.getBeansOfType(RibbonLoadBalancingHttpClient.class);
|
||||
assertThat(clients.values(), hasSize(1));
|
||||
assertThat(clients.values().toArray()[0], instanceOf(RibbonLoadBalancingHttpClient.class));
|
||||
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class);
|
||||
assertThat(lbFactorys.values(), hasSize(1));
|
||||
FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo");
|
||||
assertThat(lb, instanceOf(FeignLoadBalancer.class));
|
||||
assertThat(lb, is(not(instanceOf(RetryableFeignLoadBalancer.class))));
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -19,11 +19,18 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.util.Map;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.RetryableFeignLoadBalancer;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -37,7 +44,8 @@ import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class})
|
||||
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class})
|
||||
public class SpringRetryEnabledTests implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
@@ -47,6 +55,13 @@ public class SpringRetryEnabledTests implements ApplicationContextAware {
|
||||
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
|
||||
assertThat(factories.values(), hasSize(1));
|
||||
assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
|
||||
Map<String, RibbonLoadBalancingHttpClient> clients = context.getBeansOfType(RibbonLoadBalancingHttpClient.class);
|
||||
assertThat(clients.values(), hasSize(1));
|
||||
assertThat(clients.values().toArray()[0], instanceOf(RetryableRibbonLoadBalancingHttpClient.class));
|
||||
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class);
|
||||
assertThat(lbFactorys.values(), Matchers.hasSize(1));
|
||||
FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo");
|
||||
assertThat(lb, instanceOf(RetryableFeignLoadBalancer.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+228
-4
@@ -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);
|
||||
@@ -79,8 +113,8 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
factory.setApplicationContext(new AnnotationConfigApplicationContext(
|
||||
RibbonAutoConfiguration.class, Connections.class));
|
||||
RibbonLoadBalancingHttpClient client = factory.getClient("service",
|
||||
RibbonLoadBalancingHttpClient.class);
|
||||
RetryableRibbonLoadBalancingHttpClient client = factory.getClient("service",
|
||||
RetryableRibbonLoadBalancingHttpClient.class);
|
||||
|
||||
HttpClient delegate = client.getDelegate();
|
||||
PoolingHttpClientConnectionManager connManager = (PoolingHttpClientConnectionManager) ReflectionTestUtils.getField(delegate, "connManager");
|
||||
@@ -114,6 +148,184 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
assertThat(result.isRedirectsEnabled(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatedTimeouts()
|
||||
throws Exception {
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
RequestConfig result = getBuiltRequestConfig(Timeouts.class, null, factory);
|
||||
assertThat(result.getConnectTimeout(), is(60000));
|
||||
assertThat(result.getSocketTimeout(), is (50000));
|
||||
IClientConfig config = factory.getClientConfig("service");
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 60);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 50);
|
||||
result = getBuiltRequestConfig(Timeouts.class, null, factory);
|
||||
assertThat(result.getConnectTimeout(), is(60));
|
||||
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 RetryableRibbonLoadBalancingHttpClient 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);
|
||||
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(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);
|
||||
RetryableRibbonLoadBalancingHttpClient 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);
|
||||
RetryableRibbonLoadBalancingHttpClient 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);
|
||||
RetryableRibbonLoadBalancingHttpClient 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);
|
||||
RetryableRibbonLoadBalancingHttpClient 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 {
|
||||
|
||||
@@ -164,18 +376,30 @@ public class RibbonLoadBalancingHttpClientTests {
|
||||
|
||||
private RequestConfig getBuiltRequestConfig(Class<?> defaultConfigurationClass,
|
||||
IClientConfig configOverride) throws Exception {
|
||||
return getBuiltRequestConfig(defaultConfigurationClass, configOverride, new SpringClientFactory());
|
||||
}
|
||||
|
||||
private RequestConfig getBuiltRequestConfig(Class<?> defaultConfigurationClass,
|
||||
IClientConfig configOverride, SpringClientFactory factory)
|
||||
throws Exception {
|
||||
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
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));
|
||||
|
||||
@@ -183,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();
|
||||
}
|
||||
|
||||
|
||||
+48
-1
@@ -84,9 +84,45 @@ public class OkHttpLoadBalancingClientTests {
|
||||
assertThat(result.followRedirects(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeouts() throws Exception {
|
||||
OkHttpClient result = getHttpClient(Timeouts.class, null);
|
||||
assertThat(result.readTimeoutMillis(), is(50000));
|
||||
assertThat(result.connectTimeoutMillis(), is(60000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeoutsOverride() throws Exception {
|
||||
DefaultClientConfigImpl override = new DefaultClientConfigImpl();
|
||||
override.set(CommonClientConfigKey.ConnectTimeout, 60);
|
||||
override.set(CommonClientConfigKey.ReadTimeout, 50);
|
||||
OkHttpClient result = getHttpClient(Timeouts.class, override);
|
||||
assertThat(result.readTimeoutMillis(), is(50));
|
||||
assertThat(result.connectTimeoutMillis(), is(60));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatedTimeouts() throws Exception {
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
OkHttpClient result = getHttpClient(Timeouts.class, null, factory);
|
||||
assertThat(result.readTimeoutMillis(), is(50000));
|
||||
assertThat(result.connectTimeoutMillis(), is(60000));
|
||||
IClientConfig config = factory.getClientConfig("service");
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 60);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 50);
|
||||
result = getHttpClient(Timeouts.class, null, factory);
|
||||
assertThat(result.readTimeoutMillis(), is(50));
|
||||
assertThat(result.connectTimeoutMillis(), is(60));
|
||||
}
|
||||
|
||||
private OkHttpClient getHttpClient(Class<?> defaultConfigurationClass,
|
||||
IClientConfig configOverride) throws Exception {
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
return getHttpClient(defaultConfigurationClass, configOverride, new SpringClientFactory());
|
||||
}
|
||||
|
||||
private OkHttpClient getHttpClient(Class<?> defaultConfigurationClass,
|
||||
IClientConfig configOverride,
|
||||
SpringClientFactory factory) throws Exception {
|
||||
factory.setApplicationContext(new AnnotationConfigApplicationContext(
|
||||
RibbonAutoConfiguration.class, defaultConfigurationClass));
|
||||
|
||||
@@ -121,4 +157,15 @@ public class OkHttpLoadBalancingClientTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Timeouts {
|
||||
@Bean
|
||||
public IClientConfig clientConfig() {
|
||||
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 60000);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 50000);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
*
|
||||
* * 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.okhttp;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.ClassPathExclusions;
|
||||
import org.springframework.cloud.FilteredClassPathRunner;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(FilteredClassPathRunner.class)
|
||||
@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"})
|
||||
public class SpringRetryDisableOkHttpClientTests {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new SpringApplicationBuilder().web(false).properties("ribbon.okhttp.enabled=true")
|
||||
.sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class).run();
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if(context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
|
||||
assertThat(factories.values(), hasSize(1));
|
||||
assertThat(factories.values().toArray()[0], instanceOf(LoadBalancedRetryPolicyFactory.NeverRetryFactory.class));
|
||||
Map<String, OkHttpLoadBalancingClient> clients = context.getBeansOfType(OkHttpLoadBalancingClient.class);
|
||||
assertThat(clients.values(), hasSize(1));
|
||||
assertThat(clients.values().toArray()[0], instanceOf(OkHttpLoadBalancingClient.class));
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
*
|
||||
* * 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.okhttp;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(value = {"ribbon.okhttp.enabled: true"})
|
||||
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class})
|
||||
public class SpringRetryEnabledOkHttpClientTests implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
Map<String, LoadBalancedRetryPolicyFactory> factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
|
||||
assertThat(factories.values(), hasSize(1));
|
||||
assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
|
||||
Map<String, OkHttpLoadBalancingClient> clients = context.getBeansOfType(OkHttpLoadBalancingClient.class);
|
||||
assertThat(clients.values(), hasSize(1));
|
||||
assertThat(clients.values().toArray()[0], instanceOf(RetryableOkHttpLoadBalancingClient.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
+107
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -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 {
|
||||
}
|
||||
+55
@@ -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 {
|
||||
}
|
||||
-1
@@ -52,7 +52,6 @@ public abstract class RibbonCommandFallbackTests {
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + uri, HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
System.out.println("no fallback body: " + result.getBody());
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
*
|
||||
* * 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.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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 com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class RibbonRetryIntegrationTestBase {
|
||||
|
||||
private final Log LOG = LogFactory.getLog(RibbonRetryIntegrationTestBase.class.getName());
|
||||
|
||||
@Value("${local.server.port}")
|
||||
protected int port;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
RequestContext.getCurrentContext().clear();
|
||||
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);
|
||||
LOG.info("Response Body: " + result.getBody());
|
||||
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);
|
||||
LOG.info("Response Body: " + result.getBody());
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.2.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-netflix-dependencies</name>
|
||||
<description>Spring Cloud Netflix Dependencies</description>
|
||||
<properties>
|
||||
<archaius.version>0.7.4</archaius.version>
|
||||
<eureka.version>1.4.11</eureka.version>
|
||||
<eureka.version>1.4.12</eureka.version>
|
||||
<feign.version>9.3.1</feign.version>
|
||||
<hystrix.version>1.5.6</hystrix.version>
|
||||
<ribbon.version>2.2.0</ribbon.version>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
@@ -123,5 +123,15 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+2
-8
@@ -107,21 +107,15 @@ public class EurekaClientAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
|
||||
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
|
||||
RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(env, "eureka.instance.");
|
||||
RelaxedPropertyResolver springPropertyResolver = new RelaxedPropertyResolver(env, "spring.application.");
|
||||
String springAppName = springPropertyResolver.getProperty("name");
|
||||
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
|
||||
instance.setNonSecurePort(this.nonSecurePort);
|
||||
instance.setInstanceId(getDefaultInstanceId(this.env));
|
||||
if(StringUtils.hasText(springAppName)) {
|
||||
instance.setAppname(springAppName);
|
||||
instance.setVirtualHostName(springAppName);
|
||||
instance.setSecureVirtualHostName(springAppName);
|
||||
}
|
||||
|
||||
if (this.managementPort != this.nonSecurePort && this.managementPort != 0) {
|
||||
if (StringUtils.hasText(this.hostname)) {
|
||||
instance.setHostname(this.hostname);
|
||||
}
|
||||
RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(env, "eureka.instance.");
|
||||
String statusPageUrlPath = relaxedPropertyResolver.getProperty("statusPageUrlPath");
|
||||
String healthCheckUrlPath = relaxedPropertyResolver.getProperty("healthCheckUrlPath");
|
||||
if (StringUtils.hasText(statusPageUrlPath)) {
|
||||
|
||||
+30
-10
@@ -16,20 +16,26 @@
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.commons.util.InetUtils.HostInfo;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.appinfo.MyDataCenterInfo;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.commons.util.InetUtils.HostInfo;
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.appinfo.MyDataCenterInfo;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
@@ -37,7 +43,7 @@ import com.netflix.appinfo.MyDataCenterInfo;
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("eureka.instance")
|
||||
public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
|
||||
public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, EnvironmentAware {
|
||||
|
||||
private static final String UNKNOWN = "unknown";
|
||||
|
||||
@@ -272,6 +278,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
|
||||
private InstanceStatus initialStatus = InstanceStatus.UP;
|
||||
|
||||
private String[] defaultAddressResolutionOrder = new String[0];
|
||||
private Environment environment;
|
||||
|
||||
public String getHostname() {
|
||||
return getHostName(false);
|
||||
@@ -319,4 +326,17 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
|
||||
}
|
||||
return this.preferIpAddress ? this.ipAddress : this.hostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
// set some defaults from the environment, but allow the defaults to use relaxed binding
|
||||
RelaxedPropertyResolver springPropertyResolver = new RelaxedPropertyResolver(this.environment, "spring.application.");
|
||||
String springAppName = springPropertyResolver.getProperty("name");
|
||||
if(StringUtils.hasText(springAppName)) {
|
||||
setAppname(springAppName);
|
||||
setVirtualHostName(springAppName);
|
||||
setSecureVirtualHostName(springAppName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-3
@@ -16,7 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -24,8 +31,10 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
|
||||
|
||||
/**
|
||||
@@ -33,11 +42,31 @@ import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(DiscoveryEnabledNIWSServerList.class)
|
||||
@ConditionalOnBean(SpringClientFactory.class)
|
||||
@ConditionalOnProperty(value = "ribbon.eureka.enabled", matchIfMissing = true)
|
||||
@RibbonEurekaAutoConfiguration.ConditionalOnRibbonAndEurekaEnabled
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
|
||||
public class RibbonEurekaAutoConfiguration {
|
||||
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnRibbonAndEurekaEnabledCondition.class)
|
||||
@interface ConditionalOnRibbonAndEurekaEnabled {
|
||||
|
||||
}
|
||||
|
||||
private static class OnRibbonAndEurekaEnabledCondition extends AllNestedConditions {
|
||||
|
||||
public OnRibbonAndEurekaEnabledCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnClass(DiscoveryEnabledNIWSServerList.class)
|
||||
@ConditionalOnBean(SpringClientFactory.class)
|
||||
@ConditionalOnProperty(value = "ribbon.eureka.enabled", matchIfMissing = true)
|
||||
static class Defaults {}
|
||||
|
||||
@ConditionalOnBean(EurekaClient.class)
|
||||
static class EurekaBeans {}
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -206,6 +206,23 @@ public class EurekaClientAutoConfigurationTests {
|
||||
assertEquals("mytest", getInstanceConfig().getSecureVirtualHostName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppNameUpper() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "SPRING_APPLICATION_NAME=mytestupper");
|
||||
setupContext();
|
||||
assertEquals("mytestupper", getInstanceConfig().getAppname());
|
||||
assertEquals("mytestupper", getInstanceConfig().getVirtualHostName());
|
||||
assertEquals("mytestupper", getInstanceConfig().getSecureVirtualHostName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstanceNamePreferred() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "SPRING_APPLICATION_NAME=mytestspringappname",
|
||||
"eureka.instance.appname=mytesteurekaappname");
|
||||
setupContext();
|
||||
assertEquals("mytesteurekaappname", getInstanceConfig().getAppname());
|
||||
}
|
||||
|
||||
private void testNonSecurePort(String propName) {
|
||||
addEnvironment(this.context, propName + ":8888");
|
||||
setupContext();
|
||||
|
||||
+6
-6
@@ -16,11 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
@@ -30,9 +29,8 @@ import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.loadbalancer.ConfigurationBasedServerList;
|
||||
@@ -41,10 +39,12 @@ import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = EurekaRibbonClientPropertyOverrideIntegrationTests.TestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class EurekaRibbonClientPropertyOverrideIntegrationTests {
|
||||
@@ -72,7 +72,7 @@ public class EurekaRibbonClientPropertyOverrideIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@RibbonClients
|
||||
@Import({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
||||
@ImportAutoConfiguration({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
+5
-5
@@ -16,12 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
@@ -35,7 +34,6 @@ import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.RibbonClientPreprocessorIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -44,6 +42,8 @@ import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@@ -83,14 +83,14 @@ public class RibbonClientPreprocessorIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@RibbonClient("foo")
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class,
|
||||
@ImportAutoConfiguration({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class })
|
||||
protected static class PlainConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class,
|
||||
@ImportAutoConfiguration({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.eureka;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
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.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = RibbonEurekaAutoConfigurationTests.EurekaClientDisabledApp.class,
|
||||
properties = { "eureka.client.enabled=false", "spring.application.name=eurekadisabledtest" },
|
||||
webEnvironment = RANDOM_PORT)
|
||||
public class RibbonEurekaAutoConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
TestLoadbalancerClient testLoadbalancerClient;
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
assertThat(testLoadbalancerClient.instanceFound).isFalse();
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@EnableDiscoveryClient
|
||||
public static class EurekaClientDisabledApp {
|
||||
|
||||
@Bean
|
||||
public TestLoadbalancerClient testLoadbalanceClient(LoadBalancerClient loadBalancerClient) {
|
||||
return new TestLoadbalancerClient(loadBalancerClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(final TestLoadbalancerClient testLoadbalancerClient) {
|
||||
return new CommandLineRunner() {
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
testLoadbalancerClient.doStuff();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestLoadbalancerClient {
|
||||
|
||||
Log log = LogFactory.getLog(this.getClass());
|
||||
|
||||
private LoadBalancerClient loadBalancerClient;
|
||||
private boolean instanceFound = false;
|
||||
|
||||
public TestLoadbalancerClient(LoadBalancerClient loadBalancerClient) {
|
||||
this.loadBalancerClient = loadBalancerClient;
|
||||
}
|
||||
|
||||
public void doStuff() {
|
||||
ServiceInstance serviceInstance = loadBalancerClient.choose("http://host/doStuff");
|
||||
if (serviceInstance != null) {
|
||||
log.info("There is a service instance, because Eureka discovery is enabled and the service is registered");
|
||||
instanceFound = true;
|
||||
}
|
||||
else {
|
||||
log.warn("No instance found, because Eureka is disabled or there is no service matching.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
@@ -37,6 +37,11 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl
|
||||
if (input.getName().equals(appName)) {
|
||||
InstanceInfo instance = null;
|
||||
for (InstanceInfo info : input.getInstances()) {
|
||||
if (info.getHostName().equals(serverId)) {
|
||||
if (info.getId().equals(serverId)) {
|
||||
instance = info;
|
||||
break;
|
||||
}
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
"spring.application.name=eureka", "server.contextPath=/context" })
|
||||
"spring.application.name=eureka", "server.contextPath=/context",
|
||||
"management.security.enabled=false" })
|
||||
public class ApplicationContextTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
"spring.application.name=eureka", "server.servletPath=/servlet" })
|
||||
"spring.application.name=eureka", "server.servletPath=/servlet",
|
||||
"management.security.enabled=false" })
|
||||
public class ApplicationServletPathTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ import com.netflix.eureka.resources.ServerCodecs;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
"spring.jmx.enabled=true" })
|
||||
"spring.jmx.enabled=true", "management.security.enabled=false" })
|
||||
public class ApplicationTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
|
||||
+81
-74
@@ -1,92 +1,77 @@
|
||||
package org.springframework.cloud.netflix.eureka.server;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||
import org.springframework.cloud.netflix.eureka.server.InstanceRegistryTest.TestApplication;
|
||||
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
|
||||
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
|
||||
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRenewedEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.LeaseInfo;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
/**
|
||||
* @author Bartlomiej Slota
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = TestApplication.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = {"spring.application.name=eureka", "logging.level.org.springframework."
|
||||
+ "cloud.netflix.eureka.server.InstanceRegistry=DEBUG"})
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = {"spring.application.name=eureka", "logging.level.org.springframework."
|
||||
+ "cloud.netflix.eureka.server.InstanceRegistry=DEBUG"})
|
||||
public class InstanceRegistryTest {
|
||||
|
||||
private final List<ApplicationEvent> applicationEvents = new LinkedList<>();
|
||||
private static final String APP_NAME = "MY-APP-NAME";
|
||||
private static final String HOST_NAME = "my-host-name";
|
||||
private static final String INSTANCE_ID = "my-host-name:8008";
|
||||
private static final int PORT = 8008;
|
||||
|
||||
@SpyBean(PeerAwareInstanceRegistry.class)
|
||||
private InstanceRegistry instanceRegistry;
|
||||
|
||||
@MockBean
|
||||
private ApplicationListener<EurekaInstanceRegisteredEvent>
|
||||
instanceRegisteredEventListenerMock;
|
||||
|
||||
@MockBean
|
||||
private ApplicationListener<EurekaInstanceCanceledEvent>
|
||||
instanceCanceledEventListenerMock;
|
||||
|
||||
@MockBean
|
||||
private ApplicationListener<EurekaInstanceRenewedEvent> instanceRenewedEventListener;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
applicationEvents.clear();
|
||||
Answer applicationListenerAnswer = prepareListenerMockAnswer();
|
||||
doAnswer(applicationListenerAnswer).when(instanceRegisteredEventListenerMock)
|
||||
.onApplicationEvent(isA(EurekaInstanceRegisteredEvent.class));
|
||||
doAnswer(applicationListenerAnswer).when(instanceCanceledEventListenerMock)
|
||||
.onApplicationEvent(isA(EurekaInstanceCanceledEvent.class));
|
||||
doAnswer(applicationListenerAnswer).when(instanceRenewedEventListener)
|
||||
.onApplicationEvent(isA(EurekaInstanceRenewedEvent.class));
|
||||
this.testEvents.applicationEvents.clear();
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private TestEvents testEvents;
|
||||
|
||||
@Test
|
||||
public void testRegister() throws Exception {
|
||||
// creating instance info
|
||||
final LeaseInfo leaseInfo = getLeaseInfo();
|
||||
final InstanceInfo instanceInfo = getInstanceInfo(leaseInfo);
|
||||
final InstanceInfo instanceInfo = getInstanceInfo(APP_NAME, HOST_NAME, INSTANCE_ID, PORT, leaseInfo);
|
||||
// calling tested method
|
||||
instanceRegistry.register(instanceInfo, false);
|
||||
// event of proper type is registered
|
||||
assertEquals(1, applicationEvents.size());
|
||||
assertTrue(applicationEvents.get(0) instanceof EurekaInstanceRegisteredEvent);
|
||||
assertEquals(1, this.testEvents.applicationEvents.size());
|
||||
assertTrue(this.testEvents.applicationEvents.get(0) instanceof EurekaInstanceRegisteredEvent);
|
||||
// event details are correct
|
||||
final EurekaInstanceRegisteredEvent registeredEvent =
|
||||
(EurekaInstanceRegisteredEvent) (applicationEvents.get(0));
|
||||
(EurekaInstanceRegisteredEvent) (this.testEvents.applicationEvents.get(0));
|
||||
assertEquals(instanceInfo, registeredEvent.getInstanceInfo());
|
||||
assertEquals(leaseInfo.getDurationInSecs(), registeredEvent.getLeaseDuration());
|
||||
assertEquals(instanceRegistry, registeredEvent.getSource());
|
||||
@@ -96,12 +81,12 @@ public class InstanceRegistryTest {
|
||||
@Test
|
||||
public void testDefaultLeaseDurationRegisterEvent() throws Exception {
|
||||
// creating instance info
|
||||
final InstanceInfo instanceInfo = getInstanceInfo(null);
|
||||
final InstanceInfo instanceInfo = getInstanceInfo(APP_NAME, HOST_NAME, INSTANCE_ID, PORT, null);
|
||||
// calling tested method
|
||||
instanceRegistry.register(instanceInfo, false);
|
||||
// instance info duration is set to default
|
||||
final EurekaInstanceRegisteredEvent registeredEvent =
|
||||
(EurekaInstanceRegisteredEvent) (applicationEvents.get(0));
|
||||
(EurekaInstanceRegisteredEvent) (this.testEvents.applicationEvents.get(0));
|
||||
assertEquals(LeaseInfo.DEFAULT_LEASE_DURATION,
|
||||
registeredEvent.getLeaseDuration());
|
||||
}
|
||||
@@ -111,11 +96,11 @@ public class InstanceRegistryTest {
|
||||
// calling tested method
|
||||
instanceRegistry.internalCancel(APP_NAME, HOST_NAME, false);
|
||||
// event of proper type is registered
|
||||
assertEquals(1, applicationEvents.size());
|
||||
assertTrue(applicationEvents.get(0) instanceof EurekaInstanceCanceledEvent);
|
||||
assertEquals(1, this.testEvents.applicationEvents.size());
|
||||
assertTrue(this.testEvents.applicationEvents.get(0) instanceof EurekaInstanceCanceledEvent);
|
||||
// event details are correct
|
||||
final EurekaInstanceCanceledEvent registeredEvent =
|
||||
(EurekaInstanceCanceledEvent) (applicationEvents.get(0));
|
||||
(EurekaInstanceCanceledEvent) (this.testEvents.applicationEvents.get(0));
|
||||
assertEquals(APP_NAME, registeredEvent.getAppName());
|
||||
assertEquals(HOST_NAME, registeredEvent.getServerId());
|
||||
assertEquals(instanceRegistry, registeredEvent.getSource());
|
||||
@@ -124,40 +109,70 @@ public class InstanceRegistryTest {
|
||||
|
||||
@Test
|
||||
public void testRenew() throws Exception {
|
||||
// creating application list
|
||||
final LeaseInfo leaseInfo = getLeaseInfo();
|
||||
final InstanceInfo instanceInfo = getInstanceInfo(leaseInfo);
|
||||
final List<InstanceInfo> instances = new ArrayList<>();
|
||||
instances.add(instanceInfo);
|
||||
final Application application = new Application(APP_NAME, instances);
|
||||
//Creating two instances of the app
|
||||
final InstanceInfo instanceInfo1 = getInstanceInfo(APP_NAME, HOST_NAME, INSTANCE_ID, PORT, null);
|
||||
final InstanceInfo instanceInfo2 = getInstanceInfo(APP_NAME, HOST_NAME, "my-host-name:8009", 8009, null);
|
||||
// creating application list with an app having two instances
|
||||
final Application application = new Application(APP_NAME, Arrays.asList(instanceInfo1, instanceInfo2));
|
||||
final List<Application> applications = new ArrayList<>();
|
||||
applications.add(application);
|
||||
// stubbing applications list
|
||||
doReturn(applications).when(instanceRegistry).getSortedApplications();
|
||||
// calling tested method
|
||||
instanceRegistry.renew(APP_NAME, HOST_NAME, false);
|
||||
instanceRegistry.renew(APP_NAME, INSTANCE_ID, false);
|
||||
instanceRegistry.renew(APP_NAME, "my-host-name:8009", false);
|
||||
// event of proper type is registered
|
||||
assertEquals(1, applicationEvents.size());
|
||||
assertTrue(applicationEvents.get(0) instanceof EurekaInstanceRenewedEvent);
|
||||
assertEquals(2, this.testEvents.applicationEvents.size());
|
||||
assertTrue(this.testEvents.applicationEvents.get(0) instanceof EurekaInstanceRenewedEvent);
|
||||
assertTrue(this.testEvents.applicationEvents.get(1) instanceof EurekaInstanceRenewedEvent);
|
||||
// event details are correct
|
||||
final EurekaInstanceRenewedEvent registeredEvent = (EurekaInstanceRenewedEvent)
|
||||
(applicationEvents.get(0));
|
||||
assertEquals(APP_NAME, registeredEvent.getAppName());
|
||||
assertEquals(HOST_NAME, registeredEvent.getServerId());
|
||||
assertEquals(instanceRegistry, registeredEvent.getSource());
|
||||
assertEquals(instanceInfo, registeredEvent.getInstanceInfo());
|
||||
assertFalse(registeredEvent.isReplication());
|
||||
final EurekaInstanceRenewedEvent event1 = (EurekaInstanceRenewedEvent)
|
||||
(this.testEvents.applicationEvents.get(0));
|
||||
assertEquals(APP_NAME, event1.getAppName());
|
||||
assertEquals(INSTANCE_ID, event1.getServerId());
|
||||
assertEquals(instanceRegistry, event1.getSource());
|
||||
assertEquals(instanceInfo1, event1.getInstanceInfo());
|
||||
assertFalse(event1.isReplication());
|
||||
|
||||
final EurekaInstanceRenewedEvent event2 = (EurekaInstanceRenewedEvent)
|
||||
(this.testEvents.applicationEvents.get(1));
|
||||
assertEquals(instanceInfo2, event2.getInstanceInfo());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableEurekaServer
|
||||
protected static class TestApplication {
|
||||
@Bean
|
||||
public TestEvents testEvents() {
|
||||
return new TestEvents();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(TestApplication.class).run(args);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class TestEvents {
|
||||
public final List<ApplicationEvent> applicationEvents = new LinkedList<>();
|
||||
|
||||
@EventListener(EurekaInstanceRegisteredEvent.class)
|
||||
public void onEvent(EurekaInstanceRegisteredEvent event) {
|
||||
this.applicationEvents.add(event);
|
||||
}
|
||||
|
||||
@EventListener(EurekaInstanceCanceledEvent.class)
|
||||
public void onEvent(EurekaInstanceCanceledEvent event) {
|
||||
this.applicationEvents.add(event);
|
||||
}
|
||||
|
||||
@EventListener(EurekaInstanceRenewedEvent.class)
|
||||
public void onEvent(EurekaInstanceRenewedEvent event) {
|
||||
this.applicationEvents.add(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private LeaseInfo getLeaseInfo() {
|
||||
LeaseInfo.Builder leaseBuilder = LeaseInfo.Builder.newBuilder();
|
||||
leaseBuilder.setRenewalIntervalInSecs(10);
|
||||
@@ -165,22 +180,14 @@ public class InstanceRegistryTest {
|
||||
return leaseBuilder.build();
|
||||
}
|
||||
|
||||
private InstanceInfo getInstanceInfo(LeaseInfo leaseInfo) {
|
||||
private InstanceInfo getInstanceInfo(String appName, String hostName,
|
||||
String instanceId, int port, LeaseInfo leaseInfo) {
|
||||
InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder();
|
||||
builder.setAppName(APP_NAME);
|
||||
builder.setHostName(HOST_NAME);
|
||||
builder.setPort(8008);
|
||||
builder.setAppName(appName);
|
||||
builder.setHostName(hostName);
|
||||
builder.setInstanceId(instanceId);
|
||||
builder.setPort(port);
|
||||
builder.setLeaseInfo(leaseInfo);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Answer prepareListenerMockAnswer() {
|
||||
return new Answer() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return applicationEvents
|
||||
.add((ApplicationEvent) invocation.getArguments()[0]);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-amqp</artifactId>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<properties>
|
||||
|
||||
+2
-4
@@ -20,14 +20,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
@@ -37,7 +34,6 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.conn.PoolingClientConnectionManager;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -49,6 +45,8 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.ui.freemarker.SpringTemplateLoader;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-sidecar</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-spectator</artifactId>
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.metrics.MetricsHandlerInterceptor;
|
||||
import org.springframework.cloud.netflix.metrics.servo.ServoMonitorCache;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -160,4 +160,4 @@ class SpectatorMetricsTestController {
|
||||
ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) {
|
||||
return new ModelAndView("error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-turbine-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-turbine</artifactId>
|
||||
@@ -61,6 +61,11 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-archaius</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-atlas</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-eureka-server</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-feign</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-hystrix</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-ribbon</artifactId>
|
||||
@@ -20,14 +20,6 @@
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-spectator</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-turbine-amqp</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-turbine-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-turbine</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.2.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-zuul</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user