Use Feign with Spring WebFlux
Implementation of Feign on Spring WebClient. Brings you the best of two worlds together : concise syntax of Feign to write client side API on fast, asynchronous and non-blocking HTTP client of Spring WebClient.
feign-reactor-core : base classes and interfaces that should allow to implement alternative reactor Feign
feign-reactor-webclient : Spring WebClient based implementation of reactor Feign
feign-reactor-cloud : Spring Cloud implementation of reactor Feign (Ribbon/Hystrix)
feign-reactor-java11 : Java 11 HttpClient based implementation of reactor Feign (!! Winner of benchmarks !!)
feign-reactor-rx2 : Rx2 compatible implementation of reactor Feign (depends on feign-reactor-webclient)
feign-reactor-rx3 : Rx3 compatible implementation of reactor Feign (depends on feign-reactor-webclient)
feign-reactor-jetty : experimental Reactive Jetty client based implementation of reactor Feign (doesn't depend on feign-reactor-webclient). In future will allow to write pure Rx2 version.
- have greater reactivity level then Spring WebClient. By default don't collect body to list instead starts sending request body as stream.
- starts receiving reactive response before all reactive request body has been sent
- process Flux<
String
> correctly in request and response body
feign-reactor-spring-cloud-starter : Single dependency to have reactive feign client operabable in your spring cloud application. Uses webclient as default client implementation.
feign-reactor-bom : Maven BOM module which simplifies dependency management for all reactive feign client modules.
Write Feign API as usual, but every method of interface
- may accept
org.reactivestreams.Publisher
as body - must return
reactor.core.publisher.Mono
orreactor.core.publisher.Flux
.
@Headers({ "Accept: application/json" })
public interface IcecreamServiceApi {
@RequestLine("GET /icecream/flavors")
Flux<Flavor> getAvailableFlavors();
@RequestLine("GET /icecream/mixins")
Flux<Mixin> getAvailableMixins();
@RequestLine("POST /icecream/orders")
@Headers("Content-Type: application/json")
Mono<Bill> makeOrder(IceCreamOrder order);
@RequestLine("GET /icecream/orders/{orderId}")
Mono<IceCreamOrder> findOrder(@Param("orderId") int orderId);
@RequestLine("POST /icecream/bills/pay")
@Headers("Content-Type: application/json")
Mono<Void> payBill(Publisher<Bill> bill);
}
Build the client :
/* Create instance of your API */
IcecreamServiceApi client =
WebReactiveFeign //WebClient based reactive feign
//JettyReactiveFeign //Jetty http client based
//Java11ReactiveFeign //Java 11 http client based
.<IcecreamServiceApi>builder()
.target(IcecreamServiceApi.class, "http://www.icecreame.com")
/* Execute nonblocking requests */
Flux<Flavor> flavors = icecreamApi.getAvailableFlavors();
Flux<Mixin> mixins = icecreamApi.getAvailableMixins();
or cloud aware client :
IcecreamServiceApi client = CloudReactiveFeign.<IcecreamServiceApi>builder(WebReactiveFeign.builder())
.setLoadBalancerCommandFactory(s -> LoadBalancerCommand.builder()
.withLoadBalancer(AbstractLoadBalancer.class.cast(getNamedLoadBalancer(serviceName)))
.withRetryHandler(new DefaultLoadBalancerRetryHandler(1, 1, true))
.build())
.fallback(() -> Mono.just(new IcecreamServiceApi() {
@Override
public Mono<String> get() {
return Mono.just("fallback");
}
}))
.target(IcecreamServiceApi.class, "http://" + serviceName);
/* Execute nonblocking requests */
Flux<Flavor> flavors = icecreamApi.getAvailableFlavors();
Flux<Mixin> mixins = icecreamApi.getAvailableMixins();
Write Feign API as usual, but every method of interface
- may accept
Flowable
,Observable
,Single
orMaybe
as body - must return
Flowable
,Observable
,Single
orMaybe
.
@Headers({"Accept: application/json"})
public interface IcecreamServiceApi {
@RequestLine("GET /icecream/flavors")
Flowable<Flavor> getAvailableFlavors();
@RequestLine("GET /icecream/mixins")
Observable<Mixin> getAvailableMixins();
@RequestLine("POST /icecream/orders")
@Headers("Content-Type: application/json")
Single<Bill> makeOrder(IceCreamOrder order);
@RequestLine("GET /icecream/orders/{orderId}")
Maybe<IceCreamOrder> findOrder(@Param("orderId") int orderId);
@RequestLine("POST /icecream/bills/pay")
@Headers("Content-Type: application/json")
Single<Long> payBill(Bill bill);
Build the client :
/* Create instance of your API */
IcecreamServiceApi client = Rx2ReactiveFeign
.builder()
.target(IcecreamServiceApi.class, "http://www.icecreame.com")
/* Execute nonblocking requests */
Flowable<Flavor> flavors = icecreamApi.getAvailableFlavors();
Observable<Mixin> mixins = icecreamApi.getAvailableMixins();
There are 2 options:
ReactiveFeignBuilder
.addRequestInterceptor(ReactiveHttpRequestInterceptors.addHeader("Cache-Control", "no-cache"))
.addRequestInterceptor(request -> Mono
.subscriberContext()
.map(ctx -> ctx
.<String>getOrEmpty("authToken")
.map(authToken -> {
MultiValueMapUtils.addOrdered(request.headers(), "Authorization", authToken);
return request;
})
.orElse(request)));
You can use @RequestHeader
annotation for specific parameter to pass one header or map of headers
@RequestHeader example
You can enable auto-configuration of reactive Feign clients as Spring beans just by adding feign-reactor-spring-configuration
module to classpath.
Spring Auto-Configuration module
Sample cloud auto-configuration project with Eureka/WebFlux/ReaciveFeign
Library distributed under Apache License Version 2.0.