From e100207e8a5927593af2e5ad344d13b493d181fe Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 29 Jan 2015 15:17:58 +0000 Subject: [PATCH] Clarify and document some Feign stuff --- .../main/asciidoc/spring-cloud-netflix.adoc | 4 +- .../netflix/feign/FeignClientFactoryBean.java | 88 +++++++++++++- .../feign/FeignClientScanRegistrar.java | 4 +- .../netflix/feign/FeignConfiguration.java | 111 ------------------ .../cloud/netflix/ribbon/RibbonClient.java | 24 ++++ .../cloud/netflix/feign/FeignClientTests.java | 23 ++-- .../netflix/feign/SpringDecoderTests.java | 2 +- 7 files changed, 128 insertions(+), 128 deletions(-) delete mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index e5970a6d..9058ceb2 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -226,7 +226,7 @@ exclude it manually, e.g. in Maven The Eureka server does not have a backend store, but the service instances in the registry all have to send heartbeats to keep their -resistrations up to date (so this can be done in memory). Clients also +registrations up to date (so this can be done in memory). Clients also have an in-memory cache of eureka registrations (so they don't have to go to the registry for every single request to a service). @@ -481,7 +481,7 @@ configured server list, and you can supply the configuration like this ---- stores: ribbon: - listOfClients: example.com,google.com + listOfServers: example.com,google.com ---- [[spring-cloud-ribbon]] diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java index ef0db78b..5a2da27a 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java @@ -16,17 +16,33 @@ package org.springframework.cloud.netflix.feign; +import java.util.List; + import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; + +import feign.Client; +import feign.Contract; +import feign.Feign; +import feign.Logger; +import feign.Request; +import feign.RequestInterceptor; +import feign.Retryer; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import feign.ribbon.LoadBalancingTarget; +import feign.slf4j.Slf4jLogger; /** * @author Spencer Gibb */ @Data @EqualsAndHashCode(callSuper = false) -class FeignClientFactoryBean extends FeignConfiguration implements FactoryBean { +class FeignClientFactoryBean implements FactoryBean { private boolean loadbalance; @@ -34,6 +50,76 @@ class FeignClientFactoryBean extends FeignConfiguration implements FactoryBean requestInterceptors; + + protected Feign.Builder feign() { + Feign.Builder builder = Feign.builder() + // required values + .logger(this.logger).encoder(this.encoder).decoder(this.decoder) + .contract(this.contract); + + // optional values + if (this.logLevel != null) { + builder.logLevel(this.logLevel); + } + if (this.retryer != null) { + builder.retryer(this.retryer); + } + if (this.errorDecoder != null) { + builder.errorDecoder(this.errorDecoder); + } + if (this.options != null) { + builder.options(this.options); + } + if (this.requestInterceptors != null) { + builder.requestInterceptors(this.requestInterceptors); + } + + return builder; + } + + protected T loadBalance(Class type, String schemeName) { + return loadBalance(feign(), type, schemeName); + } + + protected T loadBalance(Feign.Builder builder, Class type, String schemeName) { + builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here? + if (this.ribbonClient != null) { + return builder.client(this.ribbonClient).target(type, schemeName); + } + else { + return builder.target(LoadBalancingTarget.create(type, schemeName)); + } + } + @Override public Object getObject() throws Exception { if (!this.schemeName.startsWith("http")) { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientScanRegistrar.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientScanRegistrar.java index 43acd0cc..411794f2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientScanRegistrar.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientScanRegistrar.java @@ -41,8 +41,8 @@ import org.springframework.util.StringUtils; /** * @author Spencer Gibb */ -public class FeignClientScanRegistrar extends FeignConfiguration implements - ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware { +public class FeignClientScanRegistrar implements ImportBeanDefinitionRegistrar, + ResourceLoaderAware, BeanClassLoaderAware { // patterned after Spring Integration IntegrationComponentScanRegistrar diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java deleted file mode 100644 index f00eb939..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013-2015 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; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.netflix.archaius.ConfigurableEnvironmentConfiguration; -import org.springframework.context.annotation.Configuration; - -import feign.*; -import feign.codec.Decoder; -import feign.codec.Encoder; -import feign.codec.ErrorDecoder; -import feign.ribbon.LoadBalancingTarget; -import feign.slf4j.Slf4jLogger; - -/** - * @author Spencer Gibb - */ -@Configuration -public class FeignConfiguration { - - @Autowired - ConfigurableEnvironmentConfiguration envConfig; // FIXME: howto enforce this? - - @Autowired - private Decoder decoder; - - @Autowired - private Encoder encoder; - - @Autowired - private Logger logger; - - @Autowired - private Contract contract; - - @Autowired(required = false) - private Logger.Level logLevel; - - @Autowired(required = false) - private Retryer retryer; - - @Autowired(required = false) - private ErrorDecoder errorDecoder; - - @Autowired(required = false) - private Request.Options options; - - @Autowired(required = false) - private Client ribbonClient; - - @Autowired(required = false) - private List requestInterceptors; - - protected Feign.Builder feign() { - Feign.Builder builder = Feign.builder() - // required values - .logger(this.logger).encoder(this.encoder).decoder(this.decoder) - .contract(this.contract); - - // optional values - if (this.logLevel != null) { - builder.logLevel(this.logLevel); - } - if (this.retryer != null) { - builder.retryer(this.retryer); - } - if (this.errorDecoder != null) { - builder.errorDecoder(this.errorDecoder); - } - if (this.options != null) { - builder.options(this.options); - } - if (this.requestInterceptors != null) { - builder.requestInterceptors(requestInterceptors); - } - - return builder; - } - - protected T loadBalance(Class type, String schemeName) { - return loadBalance(feign(), type, schemeName); - } - - protected T loadBalance(Feign.Builder builder, Class type, String schemeName) { - builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here? - if (this.ribbonClient != null) { - return builder.client(this.ribbonClient).target(type, schemeName); - } - else { - return builder.target(LoadBalancingTarget.create(type, schemeName)); - } - } - -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java index 2ad16d05..61d39b19 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java @@ -25,7 +25,15 @@ import java.lang.annotation.Target; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.ServerListFilter; + /** + * Declarative configuration for a ribbon client. Add this annotation to any + * @Configuration and then inject a {@link SpringClientFactory} to access the + * client that is created. + * * @author Dave Syer */ @Configuration @@ -35,10 +43,26 @@ import org.springframework.context.annotation.Import; @Documented public @interface RibbonClient { + /** + * Synonym for name (the name of the client) + * + * @see #name() + */ String value() default ""; + /** + * The name of the ribbon client, uniquely identifying a set of client resources, + * including a load balancer. + */ String name() default ""; + /** + * A custom @Configuration for the ribbon client. Can contain override + * @Bean definition for the pieces that make up the client, for instance + * {@link ILoadBalancer}, {@link ServerListFilter}, {@link IRule}. + * + * @see RibbonClientConfiguration for the defaults + */ Class[] configuration() default {}; } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/FeignClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/FeignClientTests.java index 56d9d67d..c1eeae75 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/FeignClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/FeignClientTests.java @@ -16,9 +16,6 @@ package org.springframework.cloud.netflix.feign; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; @@ -55,7 +52,9 @@ import com.netflix.loadbalancer.Server; import feign.RequestInterceptor; import feign.RequestTemplate; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * @author Spencer Gibb @@ -65,7 +64,7 @@ import static org.junit.Assert.*; @WebAppConfiguration @IntegrationTest({ "server.port=0", "spring.application.name=feignclienttest" }) @DirtiesContext -public class FeignClientTests extends FeignConfiguration { +public class FeignClientTests { @Value("${local.server.port}") private int port = 0; @@ -169,24 +168,26 @@ public class FeignClientTests extends FeignConfiguration { @Test public void testSimpleType() { - Hello hello = testClient.getHello(); + 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 testGenericType() { - List hellos = testClient.getHellos(); + List hellos = this.testClient.getHellos(); assertNotNull("hellos was null", hellos); assertEquals("hellos didn't match", hellos, getHelloList()); } @Test public void testRequestInterceptors() { - List headers = testClient.getHelloHeaders(); + List headers = this.testClient.getHelloHeaders(); assertNotNull("headers was null", headers); - assertTrue("headers didn't contain myheader1value", headers.contains("myheader1value")); - assertTrue("headers didn't contain myheader2value", headers.contains("myheader2value")); + assertTrue("headers didn't contain myheader1value", + headers.contains("myheader1value")); + assertTrue("headers didn't contain myheader2value", + headers.contains("myheader2value")); } @Data @@ -207,7 +208,7 @@ class LocalRibbonClientConfiguration { @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer = new BaseLoadBalancer(); - balancer.setServersList(Arrays.asList(new Server("localhost", port))); + balancer.setServersList(Arrays.asList(new Server("localhost", this.port))); return balancer; } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java index a63c584f..650117a5 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java @@ -50,7 +50,7 @@ import static org.junit.Assert.assertNotNull; @IntegrationTest({ "server.port=0", "spring.application.name=springdecodertest", "spring.jmx.enabled=true" }) @DirtiesContext -public class SpringDecoderTests extends FeignConfiguration { +public class SpringDecoderTests extends FeignClientFactoryBean { @Value("${local.server.port}") private int port = 0;