Micrometer Support (#462)
Setting up micrometer for Feign, fixes #457 This makes spring-cloud-openfeign capable of configuring any Capability (e.g.: Metrics5Capability for Dropwizard Metrics or MicrometerCapability for micrometer). This also auto-configures MicrometerCapability if a MeterRegistry is available and feign-micrometer is on the classpath.
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
|feign.httpclient.max-connections-per-route | `50` |
|
||||
|feign.httpclient.time-to-live | `900` |
|
||||
|feign.httpclient.time-to-live-unit | |
|
||||
|feign.metrics.enabled | `true` | Enables metrics capability for Feign.
|
||||
|feign.okhttp.enabled | `false` | Enables the use of the OK HTTP Client by Feign.
|
||||
|
||||
|===
|
||||
@@ -32,9 +32,9 @@ Example spring boot app
|
||||
@EnableFeignClients
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
@@ -44,14 +44,14 @@ public class Application {
|
||||
----
|
||||
@FeignClient("stores")
|
||||
public interface StoreClient {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/stores")
|
||||
List<Store> getStores();
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/stores")
|
||||
List<Store> getStores();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/stores")
|
||||
Page<Store> getStores(Pageable pageable);
|
||||
@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);
|
||||
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
|
||||
Store update(@PathVariable("storeId") Long storeId, Store store);
|
||||
}
|
||||
----
|
||||
|
||||
@@ -65,7 +65,7 @@ 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
|
||||
don't want to use Eureka, you can configure a list of servers
|
||||
in your external configuration using https://cloud.spring.io/spring-cloud-static/spring-cloud-commons/current/reference/html/#simplediscoveryclient[`SimpleDiscoveryClient`].
|
||||
|
||||
Spring Cloud OpenFeign supports all the features available for the blocking mode of Spring Cloud LoadBalancer. You can read more about them in the https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer[project documentation].
|
||||
@@ -84,7 +84,7 @@ Spring Cloud lets you take full control of the feign client by declaring additio
|
||||
----
|
||||
@FeignClient(name = "stores", configuration = FooConfiguration.class)
|
||||
public interface StoreClient {
|
||||
//..
|
||||
//..
|
||||
}
|
||||
----
|
||||
|
||||
@@ -104,7 +104,7 @@ Placeholders are supported in the `name` and `url` attributes.
|
||||
----
|
||||
@FeignClient(name = "${feign.name}", url = "${feign.url}")
|
||||
public interface StoreClient {
|
||||
//..
|
||||
//..
|
||||
}
|
||||
----
|
||||
|
||||
@@ -113,10 +113,11 @@ Spring Cloud OpenFeign provides the following beans by default for feign (`BeanT
|
||||
* `Decoder` feignDecoder: `ResponseEntityDecoder` (which wraps a `SpringDecoder`)
|
||||
* `Encoder` feignEncoder: `SpringEncoder`
|
||||
* `Logger` feignLogger: `Slf4jLogger`
|
||||
* `MicrometerCapability` micrometerCapability: If `feign-micrometer` is on the classpath and `MeterRegistry` is available
|
||||
* `Contract` feignContract: `SpringMvcContract`
|
||||
* `Feign.Builder` feignBuilder: `FeignCircuitBreaker.Builder`
|
||||
* `Client` feignClient: 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.
|
||||
* `Client` feignClient: If Spring Cloud LoadBalancer is on the classpath, `FeignBlockingLoadBalancerClient` is used.
|
||||
If none of them is on the classpath, the default feign client is used.
|
||||
|
||||
NOTE: `spring-cloud-starter-openfeign` supports `spring-cloud-starter-loadbalancer`. However, as is an optional dependency, you need to make sure it been added to your project if you want to use it.
|
||||
|
||||
@@ -132,6 +133,7 @@ Spring Cloud OpenFeign _does not_ provide the following beans by default for fei
|
||||
* `Collection<RequestInterceptor>`
|
||||
* `SetterFactory`
|
||||
* `QueryMapEncoder`
|
||||
* `Capability` (`MicrometerCapability` is provided by default)
|
||||
|
||||
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,
|
||||
@@ -143,15 +145,15 @@ Creating a bean of one of those type and placing it in a `@FeignClient` configur
|
||||
----
|
||||
@Configuration
|
||||
public class FooConfiguration {
|
||||
@Bean
|
||||
public Contract feignContract() {
|
||||
return new feign.Contract.Default();
|
||||
}
|
||||
@Bean
|
||||
public Contract feignContract() {
|
||||
return new feign.Contract.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
|
||||
return new BasicAuthRequestInterceptor("user", "password");
|
||||
}
|
||||
@Bean
|
||||
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
|
||||
return new BasicAuthRequestInterceptor("user", "password");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -163,25 +165,29 @@ application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
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
|
||||
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
|
||||
capabilities:
|
||||
- com.example.FooCapability
|
||||
- com.example.BarCapability
|
||||
metrics.enabled: false
|
||||
----
|
||||
|
||||
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.
|
||||
@@ -194,12 +200,12 @@ application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
feign:
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
connectTimeout: 5000
|
||||
readTimeout: 5000
|
||||
loggerLevel: basic
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
connectTimeout: 5000
|
||||
readTimeout: 5000
|
||||
loggerLevel: basic
|
||||
----
|
||||
|
||||
If we create both `@Configuration` bean and configuration properties, configuration properties will win.
|
||||
@@ -215,7 +221,7 @@ collision of these configuration beans.
|
||||
----
|
||||
@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
|
||||
public interface FooClient {
|
||||
//..
|
||||
//..
|
||||
}
|
||||
----
|
||||
|
||||
@@ -223,7 +229,7 @@ public interface FooClient {
|
||||
----
|
||||
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
|
||||
public interface BarClient {
|
||||
//..
|
||||
//..
|
||||
}
|
||||
----
|
||||
|
||||
@@ -279,12 +285,13 @@ class FooController {
|
||||
|
||||
private FooClient adminClient;
|
||||
|
||||
@Autowired
|
||||
public FooController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
|
||||
@Autowired
|
||||
public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerCapability micrometerCapability) {
|
||||
this.fooClient = Feign.builder().client(client)
|
||||
.encoder(encoder)
|
||||
.decoder(decoder)
|
||||
.contract(contract)
|
||||
.addCapability(micrometerCapability)
|
||||
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
|
||||
.target(FooClient.class, "https://PROD-SVC");
|
||||
|
||||
@@ -292,9 +299,10 @@ class FooController {
|
||||
.encoder(encoder)
|
||||
.decoder(decoder)
|
||||
.contract(contract)
|
||||
.addCapability(micrometerCapability)
|
||||
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
|
||||
.target(FooClient.class, "https://PROD-SVC");
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -321,7 +329,7 @@ To disable Spring Cloud CircuitBreaker support on a per-client basis create a va
|
||||
----
|
||||
@Configuration
|
||||
public class FooConfiguration {
|
||||
@Bean
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public Feign.Builder feignBuilder() {
|
||||
return Feign.builder();
|
||||
@@ -371,8 +379,8 @@ 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);
|
||||
@RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
|
||||
User getUser(@PathVariable("id") long id);
|
||||
}
|
||||
----
|
||||
|
||||
@@ -455,10 +463,63 @@ For example, the following would set the `Logger.Level` to `FULL`:
|
||||
----
|
||||
@Configuration
|
||||
public class FooConfiguration {
|
||||
@Bean
|
||||
Logger.Level feignLoggerLevel() {
|
||||
return Logger.Level.FULL;
|
||||
}
|
||||
@Bean
|
||||
Logger.Level feignLoggerLevel() {
|
||||
return Logger.Level.FULL;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Feign Capability support
|
||||
|
||||
The Feign capabilities expose core Feign components so that these components can be modified. For example, the capabilities can take the `Client`, _decorate_ it, and give the decorated instance back to Feign.
|
||||
The support for metrics libraries is a good real-life example for this. See <<feign-metrics>>.
|
||||
|
||||
Creating one or more `Capability` beans and placing them in a `@FeignClient` configuration lets you register them and modify the behavior of the involved client.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Configuration
|
||||
public class FooConfiguration {
|
||||
@Bean
|
||||
Capability customCapability() {
|
||||
return new CustomCapability();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Feign metrics
|
||||
|
||||
If all of the following conditions are true, a `MicrometerCapability` bean is created and registered so that your Feign client publishes metrics to Micrometer:
|
||||
|
||||
* `feign-micrometer` is on the classpath
|
||||
* A `MeterRegistry` bean is available
|
||||
* feign metrics properties are set to `true` (by default)
|
||||
- `feign.metrics.enabled=true` (for all clients)
|
||||
- `feign.client.config.feignName.metrics.enabled=true` (for a single client)
|
||||
|
||||
NOTE: If your application already uses Micrometer, enabling metrics is as simple as putting `feign-micrometer` onto your classpath.
|
||||
|
||||
You can also disable the feature by either:
|
||||
|
||||
* excluding `feign-micrometer` from your classpath
|
||||
* setting one of the feign metrics properties to `false`
|
||||
- `feign.metrics.enabled=false`
|
||||
- `feign.client.config.feignName.metrics.enabled=false`
|
||||
|
||||
NOTE: `feign.metrics.enabled=false` disables metrics support for *all* Feign clients regardless of the value of the client-level flags: `feign.client.config.feignName.metrics.enabled`.
|
||||
If you want to enable or disable merics per client, don't set `feign.metrics.enabled` and use `feign.client.config.feignName.metrics.enabled`.
|
||||
|
||||
You can also customize the `MicrometerCapability` by registering your own bean:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Configuration
|
||||
public class FooConfiguration {
|
||||
@Bean
|
||||
public MicrometerCapability micrometerCapability(MeterRegistry meterRegistry) {
|
||||
return new MicrometerCapability(meterRegistry);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -477,10 +538,10 @@ For example, the `Params` class defines parameters `param1` and `param2`:
|
||||
----
|
||||
// Params.java
|
||||
public class Params {
|
||||
private String param1;
|
||||
private String param2;
|
||||
private String param1;
|
||||
private String param2;
|
||||
|
||||
// [Getters and setters omitted for brevity]
|
||||
// [Getters and setters omitted for brevity]
|
||||
}
|
||||
----
|
||||
|
||||
@@ -491,8 +552,8 @@ The following feign client uses the `Params` class by using the `@SpringQueryMap
|
||||
@FeignClient("demo")
|
||||
public interface DemoTemplate {
|
||||
|
||||
@GetMapping(path = "/demo")
|
||||
String demoEndpoint(@SpringQueryMap Params params);
|
||||
@GetMapping(path = "/demo")
|
||||
String demoEndpoint(@SpringQueryMap Params params);
|
||||
}
|
||||
----
|
||||
|
||||
@@ -513,8 +574,8 @@ and deserialize HATEOAS representation models: https://docs.spring.io/spring-hat
|
||||
@FeignClient("demo")
|
||||
public interface DemoTemplate {
|
||||
|
||||
@GetMapping(path = "/stores")
|
||||
CollectionModel<Store> getStores();
|
||||
@GetMapping(path = "/stores")
|
||||
CollectionModel<Store> getStores();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -543,8 +604,8 @@ Note that both variable name and the path segment placeholder are called `matrix
|
||||
@FeignClient("demo")
|
||||
public interface DemoTemplate {
|
||||
|
||||
@GetMapping(path = "/stores")
|
||||
CollectionModel<Store> getStores();
|
||||
@GetMapping(path = "/stores")
|
||||
CollectionModel<Store> getStores();
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user