2.2.10.BUILD-SNAPSHOT
This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.
1. Declarative REST Client: Feign
Feign is a declarative web service client.
It makes writing web service clients easier.
To use Feign create an interface and annotate it.
It has pluggable annotation support including Feign annotations and JAX-RS annotations.
Feign also supports pluggable encoders and decoders.
Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters
used by default in Spring Web.
Spring Cloud integrates Ribbon and Eureka, Spring Cloud CircuitBreaker, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
1.1. How to Include Feign
To include Feign in your project use the starter with group org.springframework.cloud
and artifact id spring-cloud-starter-openfeign
. See the Spring Cloud Project page
for details on setting up your build system with the current Spring Cloud Release Train.
Example spring boot app
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.GET, value = "/stores")
Page<Store> getStores(Pageable pageable);
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
}
In the @FeignClient
annotation the String value ("stores" above) is an arbitrary client name, which is used to create either a Ribbon load-balancer (see below for details of Ribbon support and below for details of Spring Cloud CircuitBreaker support) or Spring Cloud LoadBalancer.
You can also specify a URL using the url
attribute
(absolute value or just a hostname). The name of the bean in the
application context is the fully qualified name of the interface.
To specify your own alias value you can use the qualifiers
value
of the @FeignClient
annotation.
The load-balancer client above will want to discover the physical addresses
for the "stores" service. If your application is a Eureka client then
it will resolve the service in the Eureka service registry. If you
don’t want to use Eureka, you can simply configure a list of servers
in your external configuration using SimpleDiscoveryClient
.
In order to maintain backward compatibility, is used as the default load-balancer implementation.
However, Spring Cloud Netflix Ribbon is now in maintenance mode, so we recommend using Spring Cloud LoadBalancer instead.
To do this, set the value of spring.cloud.loadbalancer.ribbon.enabled to false .
|
1.2. Overriding Feign Defaults
A central concept in Spring Cloud’s Feign support is that of the named client. Each feign client is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer using the @FeignClient
annotation. Spring Cloud creates a new ensemble as an
ApplicationContext
on demand for each named client using FeignClientsConfiguration
. This contains (amongst other things) an feign.Decoder
, a feign.Encoder
, and a feign.Contract
.
It is possible to override the name of that ensemble by using the contextId
attribute of the @FeignClient
annotation.
Spring Cloud lets you take full control of the feign client by declaring additional configuration (on top of the FeignClientsConfiguration
) using @FeignClient
. Example:
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
//..
}
In this case the client is composed from the components already in FeignClientsConfiguration
together with any in FooConfiguration
(where the latter will override the former).
FooConfiguration does not need to be annotated with @Configuration . However, if it is, then take care to exclude it from any @ComponentScan that would otherwise include this configuration as it will become the default source for feign.Decoder , feign.Encoder , feign.Contract , etc., when specified. This can be avoided by putting it in a separate, non-overlapping package from any @ComponentScan or @SpringBootApplication , or it can be explicitly excluded in @ComponentScan .
|
The serviceId attribute is now deprecated in favor of the name attribute.
|
Using contextId attribute of the @FeignClient annotation in addition to changing the name of
the ApplicationContext ensemble, it will override the alias of the client name
and it will be used as part of the name of the configuration bean created for that client.
|
Previously, using the url attribute, did not require the name attribute. Using name is now required.
|
Placeholders are supported in the name
and url
attributes.
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
//..
}
Spring Cloud OpenFeign provides the following beans by default for feign (BeanType
beanName: ClassName
):
-
Decoder
feignDecoder:ResponseEntityDecoder
(which wraps aSpringDecoder
) -
Encoder
feignEncoder:SpringEncoder
-
Logger
feignLogger:Slf4jLogger
-
Contract
feignContract:SpringMvcContract
-
Feign.Builder
feignBuilder:HystrixFeign.Builder
-
Feign.Builder
feignBuilder:FeignCircuitBreaker.Builder
-
Client
feignClient: if Ribbon is in the classpath and is enabled it is aLoadBalancerFeignClient
, otherwise if Spring Cloud LoadBalancer is in the classpath,FeignBlockingLoadBalancerClient
is used. If none of them is in the classpath, the default feign client is used.
spring-cloud-starter-openfeign supports both spring-cloud-starter-netflix-ribbon and spring-cloud-starter-loadbalancer . However, as they are optional dependencies, you need to make sure the one you want to use has been added to your project.
|
The OkHttpClient and ApacheHttpClient and ApacheHC5 feign clients can be used by setting feign.okhttp.enabled
or feign.httpclient.enabled
or feign.httpclient.hc5.enabled
to true
, respectively, and having them on the classpath.
You can customize the HTTP client used by providing a bean of either org.apache.http.impl.client.CloseableHttpClient
when using Apache or okhttp3.OkHttpClient
when using OK HTTP or org.apache.hc.client5.http.impl.classic.CloseableHttpClient
when using Apache HC5.
Spring Cloud OpenFeign does not provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:
-
Logger.Level
-
Retryer
-
ErrorDecoder
-
Request.Options
-
Collection<RequestInterceptor>
-
SetterFactory
-
QueryMapEncoder
A bean of Retryer.NEVER_RETRY
with the type Retryer
is created by default, which will disable retrying.
Notice this retrying behavior is different from the Feign default one, where it will automatically retry IOExceptions,
treating them as transient network related exceptions, and any RetryableException thrown from an ErrorDecoder.
Creating a bean of one of those type and placing it in a @FeignClient
configuration (such as FooConfiguration
above) allows you to override each one of the beans described. Example:
@Configuration
public class FooConfiguration {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
}
This replaces the SpringMvcContract
with feign.Contract.Default
and adds a RequestInterceptor
to the collection of RequestInterceptor
.
@FeignClient
also can be configured using configuration properties.
application.yml
feign:
client:
config:
feignName:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
errorDecoder: com.example.SimpleErrorDecoder
retryer: com.example.SimpleRetryer
defaultQueryParameters:
query: queryValue
defaultRequestHeaders:
header: headerValue
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
Default configurations can be specified in the @EnableFeignClients
attribute defaultConfiguration
in a similar manner as described above. The difference is that this configuration will apply to all feign clients.
If you prefer using configuration properties to configured all @FeignClient
, you can create configuration properties with default
feign name.
You can use feign.client.config.feignName.defaultQueryParameters
and feign.client.config.feignName.defaultRequestHeaders
to specify query parameters and headers that will be sent with every request of the client named feignName
.
application.yml
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
If we create both @Configuration
bean and configuration properties, configuration properties will win.
It will override @Configuration
values. But if you want to change the priority to @Configuration
,
you can change feign.client.default-to-properties
to false
.
If you need to use ThreadLocal bound variables in your RequestInterceptor`s you will need to either set the
thread isolation strategy for Hystrix to `SEMAPHORE or disable Hystrix in Feign.
|
application.yml
# To disable Hystrix in Feign
feign:
hystrix:
enabled: false
# To set thread isolation to SEMAPHORE
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
If we want to create multiple feign clients with the same name or url
so that they would point to the same server but each with a different custom configuration then
we have to use contextId
attribute of the @FeignClient
in order to avoid name
collision of these configuration beans.
@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
//..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
//..
}
It is also possible to configure FeignClient not to inherit beans from the parent context.
You can do this by overriding the inheritParentConfiguration()
in a FeignClientConfigurer
bean to return false
:
@Configuration
public class CustomConfiguration{
@Bean
public FeignClientConfigurer feignClientConfigurer() {
return new FeignClientConfigurer() {
@Override
public boolean inheritParentConfiguration() {
return false;
}
};
}
}
By default, Feign clients do not encode slash / characters. You can change this behaviour, by setting the value of feign.client.decodeSlash to false .
|
1.2.1. SpringEncoder
configuration
In the SpringEncoder
that we provide, we set null
charset for binary content types and UTF-8
for all the other ones.
You can modify this behaviour to derive the charset from the Content-Type
header charset instead by setting the value of feign.encoder.charset-from-content-type
to true
.
1.3. Timeout Handling
We can configure timeouts on both the default and the named client. OpenFeign works with two timeout parameters:
-
connectTimeout
prevents blocking the caller due to the long server processing time. -
readTimeout
is applied from the time of connection establishment and is triggered when returning the response takes too long.
In case the server is not running or available a packet results in connection refused. The communication ends either with an error message or in a fallback. This can happen before the connectTimeout if it is set very low. The time taken to perform a lookup and to receive such a packet causes a significant part of this delay. It is subject to change based on the remote host that involves a DNS lookup.
|
When Hystrix is enabled, its timeout configuration defaults to 1000 milliseconds. Hence, it might occur before the client timeout that we configured earlier. Increasing this timeout prevents it from happening.
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 60000
When the Hystrix timeout is enabled and its timeout is set longer than that of a feign client, HystrixTimeoutException wraps a feign exception. Otherwise, the only difference is the cause of the exception. The purpose of HystrixTimeoutException is to wrap any runtime exception that occurs first and throw an instance of itself.
|
1.4. Creating Feign Clients Manually
In some cases it might be necessary to customize your Feign Clients in a way that is not possible using the methods above. In this case you can create Clients using the Feign Builder API. Below is an example which creates two Feign Clients with the same interface but configures each one with a separate request interceptor.
@Import(FeignClientsConfiguration.class)
class FooController {
private FooClient fooClient;
private FooClient adminClient;
@Autowired
public FooController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "https://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "https://PROD-SVC");
}
}
In the above example FeignClientsConfiguration.class is the default configuration
provided by Spring Cloud OpenFeign.
|
PROD-SVC is the name of the service the Clients will be making requests to.
|
The Feign Contract object defines what annotations and values are valid on interfaces. The
autowired Contract bean provides supports for SpringMVC annotations, instead of
the default Feign native annotations.
|
You can also use the Builder`to configure FeignClient not to inherit beans from the parent context.
You can do this by overriding calling `inheritParentContext(false)
on the Builder
.
1.5. Feign Hystrix Support
If Hystrix is on the classpath and feign.hystrix.enabled=true
, Feign will wrap all methods with a circuit breaker. Returning a com.netflix.hystrix.HystrixCommand
is also available. This lets you use reactive patterns (with a call to .toObservable()
or .observe()
or asynchronous use (with a call to .queue()
).
To disable Hystrix support on a per-client basis create a vanilla Feign.Builder
with the "prototype" scope, e.g.:
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
Prior to the Spring Cloud Dalston release, if Hystrix was on the classpath Feign would have wrapped all methods in a circuit breaker by default. This default behavior was changed in Spring Cloud Dalston in favor for an opt-in approach. |
1.6. Feign Hystrix Fallbacks
Hystrix supports the notion of a fallback: a default code path that is executed when the circuit is open or there is an error. To enable fallbacks for a given @FeignClient
set the fallback
attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
@FeignClient(name = "hello", fallback = HystrixClientFallback.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
static class HystrixClientFallback implements HystrixClient {
@Override
public Hello iFailSometimes() {
return new Hello("fallback");
}
}
If one needs access to the cause that made the fallback trigger, one can use the fallbackFactory
attribute inside @FeignClient
.
@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
@Override
public HystrixClient create(Throwable cause) {
return new HystrixClient() {
@Override
public Hello iFailSometimes() {
return new Hello("fallback; reason was: " + cause.getMessage());
}
};
}
}
There is a limitation with the implementation of fallbacks in Feign and how Hystrix fallbacks work. Fallbacks are currently not supported for methods that return com.netflix.hystrix.HystrixCommand and rx.Observable .
|
1.7. Feign Spring Cloud CircuitBreaker Support
If Spring Cloud CircuitBreaker is on the classpath and feign.circuitbreaker.enabled=true
, Feign will wrap all methods with a circuit breaker.
To disable Spring Cloud CircuitBreaker support on a per-client basis create a vanilla Feign.Builder
with the "prototype" scope, e.g.:
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
The circuit breaker name follows this pattern <feignClientName>#<calledMethod>
. When calling a @FeignClient
with name foo
and the called interface method is bar
then the circuit breaker name will be foo_bar
.
1.8. Feign Spring Cloud CircuitBreaker Fallbacks
Spring Cloud CircuitBreaker supports the notion of a fallback: a default code path that is executed when the circuit is open or there is an error. To enable fallbacks for a given @FeignClient
set the fallback
attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
@FeignClient(name = "test", url = "http://localhost:${server.port}/",
fallback = Fallback.class)
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Component
static class Fallback implements TestClient {
@Override
public Hello getHello() {
throw new NoFallbackAvailableException("Boom!", new RuntimeException());
}
@Override
public String getException() {
return "Fixed response";
}
}
If one needs access to the cause that made the fallback trigger, one can use the fallbackFactory
attribute inside @FeignClient
.
@FeignClient(name = "testClientWithFactory", url = "http://localhost:${server.port}/",
fallbackFactory = TestFallbackFactory.class)
protected interface TestClientWithFactory {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Component
static class TestFallbackFactory implements FallbackFactory<FallbackWithFactory> {
@Override
public FallbackWithFactory create(Throwable cause) {
return new FallbackWithFactory();
}
}
static class FallbackWithFactory implements TestClientWithFactory {
@Override
public Hello getHello() {
throw new NoFallbackAvailableException("Boom!", new RuntimeException());
}
@Override
public String getException() {
return "Fixed response";
}
}
1.9. Feign and @Primary
When using Feign with Hystrix fallbacks, there are multiple beans in the ApplicationContext
of the same type. This will cause @Autowired
to not work because there isn’t exactly one bean, or one marked as primary. To work around this, Spring Cloud OpenFeign marks all Feign instances as @Primary
, so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the primary
attribute of @FeignClient
to false.
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
// methods here
}
1.10. Feign Inheritance Support
Feign supports boilerplate apis via single-inheritance interfaces. This allows grouping common operations into convenient base interfaces.
public interface UserService {
@RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
User getUser(@PathVariable("id") long id);
}
@RestController
public class UserResource implements UserService {
}
package project.user;
@FeignClient("users")
public interface UserClient extends UserService {
}
@FeignClient interfaces should not be shared between server and client and annotating @FeignClient interfaces with @RequestMapping on class level is no longer supported.
|
1.11. Feign request/response compression
You may consider enabling the request or response GZIP compression for your Feign requests. You can do this by enabling one of the properties:
feign.compression.request.enabled=true
feign.compression.response.enabled=true
Feign request compression gives you settings similar to what you may set for your web server:
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
These properties allow you to be selective about the compressed media types and minimum request threshold length.
For http clients except OkHttpClient, default gzip decoder can be enabled to decode gzip response in UTF-8 encoding:
feign.compression.response.enabled=true
feign.compression.response.useGzipDecoder=true
1.12. Feign logging
A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the DEBUG
level.
logging.level.project.user.UserClient: DEBUG
The Logger.Level
object that you may configure per client, tells Feign how much to log. Choices are:
-
NONE
, No logging (DEFAULT). -
BASIC
, Log only the request method and URL and the response status code and execution time. -
HEADERS
, Log the basic information along with request and response headers. -
FULL
, Log the headers, body, and metadata for both requests and responses.
For example, the following would set the Logger.Level
to FULL
:
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
1.13. Feign @QueryMap support
The OpenFeign @QueryMap
annotation provides support for POJOs to be used as
GET parameter maps. Unfortunately, the default OpenFeign QueryMap annotation is
incompatible with Spring because it lacks a value
property.
Spring Cloud OpenFeign provides an equivalent @SpringQueryMap
annotation, which
is used to annotate a POJO or Map parameter as a query parameter map.
For example, the Params
class defines parameters param1
and param2
:
// Params.java
public class Params {
private String param1;
private String param2;
// [Getters and setters omitted for brevity]
}
The following feign client uses the Params
class by using the @SpringQueryMap
annotation:
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/demo")
String demoEndpoint(@SpringQueryMap Params params);
}
If you need more control over the generated query parameter map, you can implement a custom QueryMapEncoder
bean.
1.14. HATEOAS support
Spring provides some APIs to create REST representations that follow the HATEOAS principle, Spring Hateoas and Spring Data REST.
If your project use the org.springframework.boot:spring-boot-starter-hateoas
starter
or the org.springframework.boot:spring-boot-starter-data-rest
starter, Feign HATEOAS support is enabled by default.
When HATEOAS support is enabled, Feign clients are allowed to serialize and deserialize HATEOAS representation models: EntityModel, CollectionModel and PagedModel.
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/stores")
CollectionModel<Store> getStores();
}
1.15. Spring @MatrixVariable Support
Spring Cloud OpenFeign provides support for the Spring @MatrixVariable
annotation.
If a map is passed as the method argument, the @MatrixVariable
path segment is created by joining key-value pairs from the map with a =
.
If a different object is passed, either the name
provided in the @MatrixVariable
annotation (if defined) or the annotated variable name is
joined with the provided method argument using =
.
- IMPORTANT
-
Even though, on the server side, Spring does not require the users to name the path segment placeholder same as the matrix variable name, since it would be too ambiguous on the client side, Spring Cloud OpenFeign requires that you add a path segment placeholder with a name matching either the
name
provided in the@MatrixVariable
annotation (if defined) or the annotated variable name.
For example:
@GetMapping("/objects/links/{matrixVars}")
Map<String, List<String>> getObjects(@MatrixVariable Map<String, List<String>> matrixVars);
Note that both variable name and the path segment placeholder are called matrixVars
.
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/stores")
CollectionModel<Store> getStores();
}
1.16. Feign CollectionFormat
support
We support feign.CollectionFormat
by providing the @CollectionFormat
annotation. You can annotate a Feign client method with it by passing the desired feign.CollectionFormat
as annotation value.
In the following example, the CSV
format is used instead of the default EXPLODED
to process the method.
@FeignClient(name = "demo")
protected interface PageableFeignClient {
@CollectionFormat(feign.CollectionFormat.CSV)
@GetMapping(path = "/page")
ResponseEntity performRequest(Pageable page);
}
Set the CSV format while sending Pageable as a query parameter in order for it to be encoded correctly.
|
1.17. Reactive Support
As the OpenFeign project does not currently support reactive clients, such as Spring WebClient, neither does Spring Cloud OpenFeign. We will add support for it here as soon as it becomes available in the core project.
Until that is done, we recommend using feign-reactive for Spring WebClient support.
1.17.1. Early Initialization Errors
Depending on how you are using your Feign clients you may see initialization errors when starting your application.
To work around this problem you can use an ObjectProvider
when autowiring your client.
@Autowired
ObjectProvider<TestFeginClient> testFeginClient;
1.18. Spring Data Support
You may consider enabling Jackson Modules for the support org.springframework.data.domain.Page
and org.springframework.data.domain.Sort
decoding.
feign.autoconfiguration.jackson.enabled=true
2. Configuration properties
To see the list of all Spring Cloud OpenFeign related configuration properties please check the Appendix page.