Clarify and document some Feign stuff

This commit is contained in:
Dave Syer
2015-01-29 15:17:58 +00:00
parent a739884207
commit e100207e8a
7 changed files with 128 additions and 128 deletions

View File

@@ -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]]

View File

@@ -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<Object> {
class FeignClientFactoryBean implements FactoryBean<Object> {
private boolean loadbalance;
@@ -34,6 +50,76 @@ class FeignClientFactoryBean extends FeignConfiguration implements FactoryBean<O
private String schemeName;
@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<RequestInterceptor> 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> T loadBalance(Class<T> type, String schemeName) {
return loadBalance(feign(), type, schemeName);
}
protected <T> T loadBalance(Feign.Builder builder, Class<T> 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")) {

View File

@@ -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

View File

@@ -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<RequestInterceptor> 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> T loadBalance(Class<T> type, String schemeName) {
return loadBalance(feign(), type, schemeName);
}
protected <T> T loadBalance(Feign.Builder builder, Class<T> 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));
}
}
}

View File

@@ -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
* <code>@Configuration</code> 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 <code>@Configuration</code> for the ribbon client. Can contain override
* <code>@Bean</code> 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 {};
}

View File

@@ -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<Hello> hellos = testClient.getHellos();
List<Hello> hellos = this.testClient.getHellos();
assertNotNull("hellos was null", hellos);
assertEquals("hellos didn't match", hellos, getHelloList());
}
@Test
public void testRequestInterceptors() {
List<String> headers = testClient.getHelloHeaders();
List<String> 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;
}

View File

@@ -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;