Compare commits

...
Author SHA1 Message Date
Ryan Baxter 5d9363b32f Version changes for 1.2.6.RELEASE 2017-03-08 13:26:29 -05:00
Ryan Baxter 08bb746c70 Removing unused import 2017-03-08 11:01:21 -05:00
Ryan Baxter 661faa8d88 Merge pull request #1762 from ryanjbaxter/add-ribbon-conditional
Adds conditional on Ribbon class to RibbonAutoConfiguration
2017-03-08 10:25:09 -05:00
Ryan Baxter 7596c1d152 Removed unused matcher 2017-03-07 20:45:20 -05:00
Ryan Baxter 3016992fb0 Adds conditional on Ribbon class. Fixes #1760. 2017-03-07 13:49:32 -05:00
Spencer Gibb bd3391ae55 Don't remove content-length header in feign ribbon.
Previously, when feign used the Ribbon RestClient, an exception was thrown if Content-Length was set. Over time, feign has moved away from RestClient, yet removing the header still happened.

 Fixes gh-1705

(cherry picked from commit 15e01df)
2017-03-06 20:16:55 -07:00
Ryan Baxter f7cf155eef using apache commons logger 2017-02-20 15:58:16 -05:00
Ryan Baxter a2ad006899 clearing request context in test 2017-02-20 14:24:52 -05:00
Ryan Baxter f0fc9df67d Porting #1709 into 1.2.x stream 2017-02-17 19:56:34 -05:00
Ryan Baxter 1270ba0edb Remove unused RetryTemplate bean 2017-02-15 17:03:15 -05:00
Ryan Baxter ac0539cdc4 Merge pull request #1689 from ryanjbaxter/zuul-retry
Makes Spring Retry optional for Zuul
2017-02-15 13:37:32 -05:00
Ryan Baxter 880a6ce4b5 Cherry picking PR #1695 2017-02-14 15:45:11 -05:00
Ryan Baxter b5a0274b58 Makes Spring Retry optional for Zuul 2017-02-13 07:22:42 -05:00
Ryan Baxter 7e6c7ee221 Merge pull request #1680 from ryanjbaxter/zuul-retry
Add Retry Logic To Zuul Using Spring Retry.  Fixes #1295
2017-02-09 15:13:00 -05:00
Ryan Baxter f1fb32cfdb Updated routes in tests 2017-02-09 11:26:54 -05:00
Ryan Baxter 9bc8bc6bbe Merge remote-tracking branch 'Upstream/1.2.x' into zuul-retry 2017-02-09 09:59:40 -05:00
Ryan Baxter 5073cd89ad Initial implementation of retry logic using Spring Retry 2017-02-09 09:59:04 -05:00
Ryan Baxter 08ba0cc72b Initial implementation of retry logic using Spring Retry 2017-02-09 09:58:25 -05:00
Eloi ee953bef07 delegate to the library httpclient parse the content type (#1664)
Fixes gh-1663
2017-02-06 13:01:43 -07:00
Dave Syer 4ca231aff9 Revert to snapshots 2017-02-03 11:59:36 +00:00
Dave Syer 4ee2055d5c Update to 1.2.5.RELEASE 2017-02-03 11:45:30 +00:00
Spencer Gibb 1fffc923e1 Remove mocks for application event listeners.
fixes gh-1616
2017-01-27 12:23:22 -07:00
Spencer Gibb b41027191e Make test forward compatible with boot 1.5 2017-01-27 10:51:08 -07:00
Spencer Gibb 0df5957f6b Make test forward compatible with boot 1.5 2017-01-27 10:13:23 -07:00
Spencer Gibb 234c4c65ae Handle content-type with charset.
A new version of httpclient introduced by boot changed the behaviour that previously allowed the charset. It now has to be parsed out and passed to the apache `ContentType` object explicitly.

fixes gh-1649
2017-01-27 08:44:40 -07:00
Marcin Grzejszczak c5117a8573 Making the prod code compile against boot 1.5.0 2017-01-23 12:38:26 +01:00
Bertrand Renuart f304ab7101 Use a fixed size buffer for the copy operation (#1538)
Set the default buffer size to 8192 to match Tomcat and Jetty defaults
Use a buffer per worker thread and use it for subsequent requests
2017-01-11 13:46:20 -05:00
Ryan Baxter f591136d80 Bumping versions for next release 2017-01-11 12:02:05 -05:00
Ryan Baxter 9170365b85 Revert "Version changes for 1.2.4.RELEASE"
This reverts commit f5c6920843.
2017-01-11 11:53:11 -05:00
Ryan Baxter f5c6920843 Version changes for 1.2.4.RELEASE 2017-01-11 10:50:35 -05:00
Ryan Baxter 470d6abf63 Merge pull request #1457 from ryanjbaxter/feign-retry
Use Spring Retry For Failed Requests Using Feign
2017-01-09 20:10:38 -05:00
Biju Kunjummen f3275c160b Fix null instanceinfo on EurekaInstanceRenewedEvent (#1574)
Fixes gh-1546
2017-01-09 11:26:22 -07:00
Ryan Baxter 5cd39df5e7 No longer use RetryTemplate bean since the retry policy being used is dependent on the request. We don’t want two Feign requests stomping on each other by switching out the retry policy for the template so each request now created a new template to use. 2016-12-22 11:15:29 -05:00
Ryan Baxter a91815aa43 Made Retryer a bean. 2016-12-08 19:51:44 -05:00
Ryan Baxter 0f7f88f89b Merge pull request #1519 from ryanjbaxter/config-value-changes-in-ribbon-1418
Use configuration from IConfigClient reference to always use the most up to date configuration
2016-12-01 20:26:53 -05:00
Ryan Baxter 2487ef2fad Fixed import ordering 2016-12-01 20:03:26 -05:00
Ryan Baxter b5195b7b86 Removed unnecessary null check and fixed imports 2016-12-01 20:00:55 -05:00
Ryan Baxter 845590e46c Uses configuration from IConfigClient reference to always use the most up to date configuration. Fixes #1481. 2016-12-01 14:24:23 -05:00
Spencer Gibb 7fdd86e69c Don't override values with defaults.
Defaults were set too late and were overriding values set in the environment.

Specifically, spring.application.name took precedence over eureka.instance.appname.

 fixes gh-1516
2016-11-30 11:36:32 -07:00
Spencer Gibb d1f8c03110 Fix Eureka Ribbon Auto Config conditions.
Specifically, add `@ConditionalOnBean(EurekaClient.class)`.

Fixes an issue where eureka and ribbon are on the classpath, eureka.client.enabled=false which would cause a bean not found exception.

fixes gh-1511
2016-11-29 14:29:40 -07:00
Ryan Baxter 728ef05bf3 Updated generated README 2016-11-23 17:12:42 -05:00
Ryan Baxter 27a2c31241 Bump version for next release 2016-11-23 17:12:28 -05:00
Ryan Baxter 3034c55b13 Revert "Version changes for 1.2.3 RELEASE"
This reverts commit cdaa97c006.
2016-11-23 17:03:25 -05:00
Ryan Baxter cdaa97c006 Version changes for 1.2.3 RELEASE 2016-11-23 15:33:42 -05:00
Ryan Baxter f563fcb4cd Upgrading Eureka to 1.4.12. Fixes #1429. 2016-11-22 19:49:16 -05:00
Ryan Baxter 14dd2d2359 -Addressed problem where choose server was being called multiple times per request
-Fixed imports
-Addressed other code review concerns
2016-11-11 12:15:53 -05:00
Ryan Baxter 8b4469db31 Removing commented out code 2016-11-10 10:00:00 -05:00
Ryan Baxter 658f29ed27 Merge remote-tracking branch 'Upstream/1.2.x' into feign-retry 2016-11-09 13:16:22 -05:00
Ryan Baxter 19556dc25d Merge pull request #1450 from ryanjbaxter/servlet-3.0.1-compat-1.2.x
Make Netflix Servlet 3.0.1 compatible
2016-11-09 11:56:06 -05:00
Ryan Baxter b31f6c5c4f Figure out whether servlet api 3.1 is available or not once 2016-11-09 11:09:26 -05:00
Ryan Baxter dfcc319ba7 Make Netflix Servlet 3.0.1 compatible. Fixes #1447 2016-11-07 16:11:41 -05:00
Ryan Baxter fad2c5d400 Merge remote-tracking branch 'Upstream/1.2.x' into feign-retry 2016-11-03 10:51:31 -04:00
Ryan Baxter 3201234ee1 Version changes for next release 2016-11-01 20:15:26 -04:00
Ryan Baxter 9b64d0da8f Revert "Version changes for 1.2.2 release"
This reverts commit be8eece91e.
2016-11-01 20:05:07 -04:00
Ryan Baxter be8eece91e Version changes for 1.2.2 release 2016-11-01 15:36:32 -04:00
Ryan Baxter 7c48814234 Switched media type to application/json 2016-10-27 15:46:42 -06:00
Ryan Baxter 72ccf2e649 Added route config 2016-10-27 15:46:36 -06:00
Ryan Baxter 3df42159fb Fixes #1429 2016-10-27 15:46:29 -06:00
Spencer Gibb 45e7a088bb Move initalizing appname, etc.. to afterPropertiesSet 2016-10-27 15:17:18 -06:00
Spencer Gibb 0f0c6b6204 Revert to deprecated boot APIs to support boot 1.3 2016-10-27 08:29:30 -06:00
Ryan Baxter 88a36b38e9 Initial commit for feign retry 2016-10-10 14:15:58 -04:00
86 changed files with 2557 additions and 341 deletions
+2 -2
View File
@@ -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
View File
@@ -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`.
+5 -5
View File
@@ -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>
+1 -1
View File
@@ -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,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
@@ -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;
}
@@ -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
}
}
}
@@ -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;
}
}
}
@@ -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,
@@ -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));
}
}
@@ -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() {
@@ -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);
@@ -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;
@@ -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
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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();
@@ -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);
}
}
}
@@ -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(),
@@ -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;
/**
@@ -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;
}
}
@@ -170,7 +170,6 @@ public class FormBodyWrapperFilter extends ZuulFilter {
return this.contentLength;
}
@Override
public long getContentLengthLong() {
return getContentLength();
}
@@ -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 {
@@ -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 {
@@ -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));
}
@@ -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
@@ -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
@@ -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 {
@@ -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() {
@@ -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());
}
}
@@ -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")
@@ -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);
@@ -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();
}
@@ -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));
}
}
}
@@ -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();
}
}
@@ -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);
@@ -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))));
}
}
@@ -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
@@ -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();
}
@@ -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;
}
}
}
@@ -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));
}
}
@@ -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;
}
}
@@ -0,0 +1,107 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon.support;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* @author Ryan Baxter
*/
public class ContextAwareRequestTest {
private RibbonCommandContext context;
private ContextAwareRequest request;
@Before
public void setUp() throws Exception {
context = mock(RibbonCommandContext.class);
doReturn("GET").when(context).getMethod();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.put("header1", Collections.<String>emptyList());
headers.put("header2", Arrays.asList("value1", "value2"));
headers.put("header3", Arrays.asList("value1"));
doReturn(headers).when(context).getHeaders();
doReturn(new URI("http://foo")).when(context).uri();
doReturn("foo").when(context).getServiceId();
doReturn(new LinkedMultiValueMap<>()).when(context).getParams();
request = new TestContextAwareRequest(context);
}
@After
public void tearDown() throws Exception {
context = null;
request = null;
}
@Test
public void getContext() throws Exception {
assertEquals(context, request.getContext());
}
@Test
public void getMethod() throws Exception {
assertEquals(HttpMethod.GET, request.getMethod());
}
@Test
public void getURI() throws Exception {
assertEquals(new URI("http://foo"), request.getURI());
RibbonCommandContext badUriContext = mock(RibbonCommandContext.class);
doReturn(new LinkedMultiValueMap()).when(badUriContext).getHeaders();
doReturn("foobar").when(badUriContext).getUri();
ContextAwareRequest badUriRequest = new TestContextAwareRequest(badUriContext);
assertNull(badUriRequest.getURI());
}
@Test
public void getHeaders() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.put("header1", Collections.<String>emptyList());
headers.put("header2", Arrays.asList("value1", "value2"));
headers.put("header3", Arrays.asList("value1"));
assertEquals(headers, request.getHeaders());
}
static class TestContextAwareRequest extends ContextAwareRequest {
public TestContextAwareRequest(RibbonCommandContext context) {
super(context);
}
}
}
@@ -0,0 +1,54 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.apache;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonRetryIntegrationTestBase;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RibbonRetryIntegrationTestBase.RetryableTestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.retryable: false", /* Disable retry by default, have each route enable it */
"hystrix.command.default.execution.timeout.enabled: false", /* Disable hystrix so its timeout doesnt get in the way */
"ribbon.ReadTimeout: 1000", /* Make sure ribbon will timeout before the thread is done sleeping */
"zuul.routes.retryable: /retryable/**",
"zuul.routes.retryable.retryable: true",
"retryable.ribbon.OkToRetryOnAllOperations: true",
"retryable.ribbon.MaxAutoRetries: 1",
"retryable.ribbon.MaxAutoRetriesNextServer: 1",
"zuul.routes.getretryable: /getretryable/**",
"zuul.routes.getretryable.retryable: true",
"getretryable.ribbon.MaxAutoRetries: 1",
"getretryable.ribbon.MaxAutoRetriesNextServer: 1",
"zuul.routes.disableretry: /disableretry/**",
"zuul.routes.disableretry.retryable: false", /* This will override the global */
"disableretry.ribbon.MaxAutoRetries: 1",
"disableretry.ribbon.MaxAutoRetriesNextServer: 1",
"zuul.routes.globalretrydisabled: /globalretrydisabled/**",
"globalretrydisabled.ribbon.MaxAutoRetries: 1",
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1"
})
@DirtiesContext
public class HttpClientRibbonRetryIntegrationTests extends RibbonRetryIntegrationTestBase {
}
@@ -0,0 +1,55 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.okhttp;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonRetryIntegrationTestBase;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RibbonRetryIntegrationTestBase.RetryableTestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.retryable: false", /* Disable retry by default, have each route enable it */
"ribbon.okhttp.enabled: true",
"hystrix.command.default.execution.timeout.enabled: false", /* Disable hystrix so its timeout doesnt get in the way */
"ribbon.ReadTimeout: 1000", /* Make sure ribbon will timeout before the thread is done sleeping */
"zuul.routes.retryable: /retryable/**",
"zuul.routes.retryable.retryable: true",
"retryable.ribbon.OkToRetryOnAllOperations: true",
"retryable.ribbon.MaxAutoRetries: 1",
"retryable.ribbon.MaxAutoRetriesNextServer: 1",
"zuul.routes.getretryable: /getretryable/**",
"zuul.routes.getretryable.retryable: true",
"getretryable.ribbon.MaxAutoRetries: 1",
"getretryable.ribbon.MaxAutoRetriesNextServer: 1",
"zuul.routes.disableretry: /disableretry/**",
"zuul.routes.disableretry.retryable: false", /* This will override the global */
"disableretry.ribbon.MaxAutoRetries: 1",
"disableretry.ribbon.MaxAutoRetriesNextServer: 1",
"zuul.routes.globalretrydisabled: /globalretrydisabled/**",
"globalretrydisabled.ribbon.MaxAutoRetries: 1",
"globalretrydisabled.ribbon.MaxAutoRetriesNextServer: 1"
})
@DirtiesContext
public class OkHttpRibbonRetryIntegrationTests extends RibbonRetryIntegrationTestBase {
}
@@ -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());
}
}
@@ -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));
}
}
}
@@ -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));
}
}
+3 -3
View File
@@ -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>
+11 -1
View File
@@ -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>
@@ -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)) {
@@ -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);
}
}
}
@@ -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 {}
}
}
@@ -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();
@@ -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 {
@@ -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 {
@@ -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.");
}
}
}
}
+6 -1
View File
@@ -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>
@@ -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;
}
@@ -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}")
@@ -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}")
@@ -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}")
@@ -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]);
}
};
}
}
}
+1 -1
View File
@@ -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>
@@ -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
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
@@ -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");
}
}
}
+1 -1
View File
@@ -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>
+6 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -9
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>