The Netflix stack, using Spring Boot - Part 3: Feign

in #java8 years ago (edited)

Netflix has always been a proud contributor to the open source world. It's fascinating to see how each of their libraries facilitate a lot of tasks and can help create your development in a tremendous way.In this series of blogposts - The Netflix stack, using Spring Boot - I'll be going over some of the libraries which Netflix has created and how to incorporate them in your spring applications. As always, it'll be more of a hands-on experience, as this blogpost will basically just be an overview of what you can find in the accompanying repository

Feign

In part 1, we looked at Eureka. We created a microservice that could register itself on the Eurkeka Server and created an API that would not only register itself on the Eureka server, but also find our other micro services using the registry. Last week we had a hands-on experience with Hystrix, the circuit breaker.There were a lot of replies of people asking how we could use the registry to find our microservices, but also use the response from Eureka to actually call our micro services. Today i'll be talking about how Feign will aid you in creating rest clients for all of your services, with minimal configuration and code.

Feign is a java to http client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal was reducing the complexity of binding Denominator uniformly to http apis regardless of restfulness.

If you know Retrofit, you'll see it is very easy to create rest clients using Feign. Feign is also a declarative web service client. The beauty of the entire Spring Boot Feign stack is how it can seamlessly be combined with the other libraries we discussed and will be discussing in future posts.

A small word on Ribbon

In my initial itinerary, I planned on talking about Ribbon and Spring Cloud Ribbon in part 3 of this series. However, the actual use case in our examples would be calling rest endpoints. Ribbon could do a whole lot more than that and therefore I decided that I might do a different blogpost in the end using a different examples to show more of Ribbon's capabilities.As a result, I'm going to talk about Feign (well, actually Spring Cloud Netflix Feign), which uses Ribbon under the hood to load balance our requests and is a perfect library for the examples we're creating.

The Configuration

I won't post the entire configuration of our application, as it would just bloat this blogpost with unnecessary code. If you'd like to see what an entire application in Spring Boot looks like, just head over to the repository to check it out

build.gradle
compile 'org.springframework.cloud:spring-cloud-starter-feign'  
Activating Feign Clients

With Feign added on the classpath, only 1 annotation is needed to make everything work with default configuration properties.

@EnableFeignClients
Creating a Rest Client

Creating a Rest Client is really easy, and most of the time, all we need to do is create an interface and add some annotations. Our environment will create the implementation at runtime, find the endpoint from our Eureka registry and delegate it to the proper services through the Ribbon load balancer.

@FeignClient("http://notification-service")
public interface NotificationVersionResource {  
   @RequestMapping(value = "/version", method = GET)
   String version();
}

http://notification-service isn't a randomly chosen name, and you can probably guess at this point why. Since our micro service is a Eureka Client, Ribbon will look for an entry in the registry, andtranslate it to the proper hostname or ip and port. If you remember last blogpost, we registered our notification microservice as notification-service.

Feign Client with HystrixObservable wrapper

With Hystrix on the classpath, you can also return a HystrixCommand, which you can then use synchronously or asynchronously as an Observable in your design.

@FeignClient("http://notification-service")
public interface NotificationVersionResource {  
   @RequestMapping(value = "/version", method = GET)
   HystrixObservable<String> version();
}
Feign Client with Hystrix Fallback

Last time we discussed Hystrix and how we could write fallback methods. Feign Clients have direct support for fallbacks. Simplyimplement the interface with the fallback code, which will then be used when the actual call to the endpoint delivers an error.

@FeignClient("http://notification-service")
public interface NotificationResource {  
   @RequestMapping(value = "/notifications", method = GET)
   List<Notification> findAll();
}
public class NotificationResourceImpl implements NotificationResource {  
   @Override
   public List<Notification> findAll() {
       return new ArrayList<>();
   }
}
Accessing External APIs

So far, we used Feign to create clients for our own services, which are registered on our Eureka Server using a service name. It's not unusual that you'd want to implement an external rest endpoint, or simply an endpoint that's not discoverable by Eureka. In that case, you can use the url property on the @FeignClient annotation, which gracefully supports property injection.Here's an example of how you'd create a rest client for the java subreddit1.

@FeignClient(name = "reddit-service", url = "${com.deswaef.reddit.url}")
public interface RedditResource {  
   @RequestMapping(method = RequestMethod.GET, value = "/java.json")
   RedditResponse posts();
}

Optional Configuration

I won't be going over each and every single configuration. I believe that once you're all set and you have a working example, configuration can be found in documentation, which is actually very good.However, some caveats which are discussed in the documentation are rather important. In particular, the way you can define a specific configuration for a feign client is something you need to take good care of, because if you don't pay close attention to what you're doing, the application can behave in an undesired way.By default, Spring Cloud Netflix provides:

  • Decoder feignDecoder: ResponseEntityDecoder
  • Encoder feignEncoder: SpringEncoder
  • Logger feignLogger: Slf4jLogger
  • Contract feignContract: SpringMvcContract
  • Feign.Builder feignBuilder: HystrixFeign.Builder

However, it does not provide:

  • Logger.Level
  • Retryer
  • ErrorDecoder
  • Request.Options
  • Collection

If you need one of the beans which are not provided yet, or you want to override the default provided beans, you can create a configuration per FeignClient contract, like we did in the following example.Rest Client Class

@FeignClient(
   name = "reddit-service",
   url = "${com.deswaef.reddit.url}",
   configuration = RedditFeignConfiguration.class
)

Configuration Class

@Configuration
public class RedditFeignConfiguration {  
   public static final int FIVE_SECONDS = 5000;
   @Bean
   public Logger.Level feignLogger() {
       return Logger.Level.FULL;
   }
   @Bean
   public Request.Options options() {
       return new Request.Options(FIVE_SECONDS, FIVE_SECONDS);
   }
}

The big caveat with this configuration is that your actual configuration class has to be annotated with @Configuration to support injection and context. However, if this configuration class is on the component scan path, it'll be also picked up as general configuration. This means that a configuration class like this, when also scanned by our automatic component scan, will override all of the beans for each and every FeignClient, not just the one which defined it as configuration.As a result, you should place it inside a package that isn't a candidate for a component scan, like we did in the repository.

The Github Repository

As we said before, this is not just an ordinary blogpost. It's more of a guide on how to set up your environment to quickly start working with the discussed technology. That's why we're always making sure we have an accompanying github repository available, so people can easily see how it works and have a working example at hand.The repository that's accompanying this blogpost is a bit different. Each time I'm releasing an new part of this series of blogposts, the repository will have a new branch, which will contain the new technology that will be discussed. In the end, I'll hope to come up with a nice example of how all the technologies can work together.One important note to make is that I keep updating my repositories of all my previous blogposts. You'll notice that I've updated the Spring-Cloud-Netflix version between blogpost 2 and this one, to keep up with current development.The repository can be found here.

  1. at the time of writing, the spring cloud team released version 1.1.0.RC, which resulted in a few breaking changes in our codebase. If you're interested in what was changed (don't worry, it wasn't much), you can check PR#1. It might be good to know that, at the time of writing this blogpost, the versions of the individual Netflix libraries in Spring Cloud Netflix are up to date with the latest version. If you had any problems trying out some features listed in the official documentation of Netflix OSS, chances are they should be resolved now, because we're using the latest version. ↩