Feign in Action

Igor Laborie

Java REST client with Feign

whoami

Igor Laborie

Expert Java & Web, MonkeyPatch
@ilaborie
igor@monkeypatch.io

Roadmap

  1. Basic
    • CRUD operations, JSON
  2. Advanced
    • RxJava & Circuit Breaker with Hystrix
    • HTTP/2 with Java 9

Basic

whois feign

https://github.com/OpenFeign/feign

Open Source: license Apache 2.0

A Netflix Project

Java 7+, Java 8 friendly

Not opinionated

Most Java applications require a persistent class representing felines.

Hibernate Documentation ...

Most Java applications require a microservice for felines.

Any buzzword addicts

curl http://cats/swagger

Swagger Documentation

Run with: docker run -p 8080:8080 ilaborie/cats-microservice
See code on github

Live coding

CRUD operations, JSON

See code on github

Maven dependencies

Current lastest release: 9.3.1

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-core</artifactId>
  <version>${feign.version}</version>
</dependency>
<dependency>
  <groupId>io.github.openfeign</>roupId>
  <artifactId>feign-gson</artifactId>
  <version>${feign.version}</version>
</dependency>
<dependency>
  <groupId>io.github.openfeign</>roupId>
  <artifactId>feign-slf4j</artifactId>
  <version>${feign.version}</version>
</dependency>

Interface

import feign.*;
import java.util.List;
 
@Headers("Content-Type: application/json")
public interface CatClient {
  @RequestLine("GET /")
  List<Cat> findAll();
 
  @RequestLine("GET /{id}")
  Cat findById(@Param("id") String id);
 
  @RequestLine("POST /")
  Cat create(Cat newCat);
 
  @RequestLine("DELETE /{id}")
  Cat delete(@Param("id") String id);
}

Feign Builder

CatClient client = Feign.builder()
  // Create request body
  .encoder(new GsonEncoder())
  // Transform response body
  .decoder(new GsonDecoder())
  // Add custom header
  .requestInterceptor(template -> template.header("Date", Instant.now().toString()))
  // Tips: debug lite
  .requestInterceptor(System.out::println)
  // Tips: debug with logger
  .logLevel(Logger.Level.FULL)
  .logger(new Slf4jLogger())
  .target(CatClient.class, url);

Encoders & Decoders

Under the hood

No Dark Magic
The java.lang.reflect.Proxy allow you to dynamically implement the interface.

Use the java.net.HttpURLConnection to send and process HTTP requests. (by default)

Use the reflection API to extract meta-data.
If you prefer JAX-RS annotation you can use feign-jaxrs.

Advanced

How to be async ?

Do not write a custom feign.Decoder!

You can use Java 8 CompletableFuture

Or the feign-hystrix provide RxJava support

POC implementation with Reactor core 3 on my feign-reactor

Circuit Breaker

By Bidgee (Own work) CC BY-SA 3.0, via Wikimedia Commons

Live coding

Async with ReactiveX, & Circuit Breaker with Hystrix

See code on github

With Hystrix

// Fallback implementation
CatClient fallback = () -> {
    Cat cat = new Cat("Chuck Norris's cat", CatRace.Sphynx);
    return Observable.just(Collections.singletonList(cat));
};
 
// Get Client with CircuitBreaker
CatClient client = HystrixFeign.builder()
    // ...
    .target(CatClient.class, url, fallback);
 
// Find All every 2 seconds
Observable.interval(2, SECONDS)
    .flatMap(i -> client.findAll()) // to Observable<List<Cat>>
    .flatMap(Observable::from) // unfold to Observable<Cat>
    .forEach(System.out::println);

JEP 110: HTTP/2 Client

java.net.http, See The New JDK HTTP Client API Room 9, Thursday from 09:30 til 10:30, by Michael McMahon

URI uri = new URI("http://127.0.0.1:8080/hello")
HttpResponse resp = HttpRequest.create(uri)
    .GET().response();
int statusCode = resp.statusCode();
String body = resp.body(HttpResponse.asString());
System.out.printf("[%s] %s\n", statusCode, body);

Live coding

Custom feign.Client with Java 9

See code on github

Custom Client with Java 9

HttpRequest.Builder builder = HttpRequest.create(new URI(request.url()));
// Handle body
byte[] bytes = request.body();
boolean isBodyEmpty = bytes == null || bytes.length == 0;
builder = builder.body(isBodyEmpty?noBody():fromByteArray(bytes));
// Apply headers
for (Map.Entry<String, Collection&tl;String>> entry : request.headers().entrySet()) {
    String values = entry.getValue().stream().collect(Collectors.joining(", "));
    builder = builder.setHeader(entry.getKey(), values);
}
// Execute request
HttpResponse response = builder.method(request.method()).response();
 
// Build Feign response
return Response.builder()
    .status(response.statusCode())
    .headers(mapList2MapCollection(response.headers().map()))
    .body(response.body(HttpResponse.asByteArray()))
    .build();
  

feign.Client implementation

So why feign:

Open & active community

Simple & intuitive

Extensible, useful plugins, easy to write your own

Not opinionated

Question

Any Question ?

http://bit.ly/devoxx-feign