Saturday, 30 January 2016

Orchestrating your microservices with Eureka and Feign


Introduction


This is a well known scenario: It´s time to add some new functionality to your application and you decide to add a new member to the family of micro-services.
In this example, we are going to use a service called "Sparkker" that will consume stock quotations from anothed service called "Stokker" and will publish the results to another service called "Portfolio-Manager"

This is a diagram showing the whole picture:



It´s obvious that the number of lines connecting the services increase and increase, making the maintenance and configuration of the system very tedious. How can we solve it? We can´t spend the whole day coding integration code, we need to deliver added value!

One possible solution to all this mess is to use some of the components from the Netflix Stack that have been incorporated to the Spring Cloud project and that are very simple to use within Spring Boot. Interested?


Adding an Eureka support to your Spring Boot modules


Eureka! Eureka! (Ancient greek: I found it! I found it!) is a common interjection said by someone eagerly looking for something and the very appropriate name given to a Service Discovery agent withing the Netflix Stack.

What does it do? Very simple. Basically it knows where all the services in an application are, and regularly pings them to see if they are alive or not. Also it can handle many instances of the same service and perform a basic kind of load balancing.

So, whenever a service needs another one, instead of having it configured in its internals settings; it just asks Eureka about it. The result: The configuration settings are reduced to just one: The IP and port of the Eureka Server. Neat!

How can I use it with Spring Boot? Very easy.

Add the Eureka Starter maven dependency (to both server and clients):

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
view raw Pom.xml hosted with ❤ by GitHub

Then add these two annotations to your server and client modules

@SpringBootApplication
// ... some annotations ommitted for brevity
@EnableEurekaServer
@EnableEurekaClient
public class PortfolioManagerApplication {
public static void main(String[] args) {
SpringApplication.run(PortfolioManagerApplication.class, args);
}
// ... ommitted
}
@SpringBootApplication
@EnableEurekaClient
// ... ommitted
public class SparkkerApplication {
public static void main(String[] args) {
SpringApplication.run(SparkkerApplication.class, args);
}
}

Note: An application can act both as a server and as a client. Why? Because if there are not too many modules and the load of the service discovery is not too high, a regular module can do its own tasks plus the task of the Eureka server.

If we are dealing with many modules, we might want a different JVM to do this work only. You even have the possibility of having several JVM working as servers (knows as peer mode). More info here.

Finally, add the following settings to both your client and server

spring:
application:
name: [name_of_service_being_registered]
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9999/eureka/
view raw eureka.yml hosted with ❤ by GitHub

And you are good to go! Eureka will handle the sending of hearbeat pings to the clients, registration and de-registration of new clients and the propagation of clients registry to all the services in the system!

Easing up the development with Feign - Declarative REST clients


What would be the traditional approach (in Spring) to make a query to a remote rest service? Something along these lines:

// Retrive from the settings the host,port and endpoint of your client
String host,port,endpoint;
// Build an URI object from the above parameters (and hadle it properly)
URI uri = URI.create("http://" + host + ":" + port + "endpoint")
// Build a Rest template (bear in mind that you might need to indicate some custom serialization)
RestTemplate rt = new RestTemplate(/* Do you need some custom HttpMessageConverter?*/);
// Perform the query
// Warning: Do you know that is the signature of the method being called?
// Does it return a single object? A list of them?
// You need to control that as well!
StockQuotation resutl = rt.getForObject(uri, StockQuotation.class)

However, once all of our micro-services are within the Eureka scope, this cumbersome task gets much easier using the Feign Declarative Rest Clients:

@Component
/* By using this annotation, you are indicating that we want to consume an application called "stokker"
* Is transparent to us where is the application located and whether it has many instances serving the request
*/
@FeignClient("stokker")
public interface StokkerClient {
/* You just need to indicate the relative URL of the endopoint being called and pass
* the same parameters shown in the endpoint description
*/
@RequestMapping(method = RequestMethod.GET, value = "/stockQuotationJPAs/search/findValueByStock")
List<StockQuotation> getAllStockQuotations(@RequestParam("ticker") String stock);
@RequestMapping(method = RequestMethod.GET, value = "/stockQuotationJPAs/search/findTopByStockOrderByTimestampDesc")
StockQuotation getLastStockPrice(@RequestParam("ticker") String stock);
}

Easy right? Then, you just need to inject the component in your classes and invoke it as any regular Spring bean. For instance, in Sparkker, we have:

  • A controller that receives the customer requests indicating that a new analysis on a stock is required.
  • Using the Feign Client, we will get the data from Stokker.
  • Finally, we will invoke our Spark-based analysis service to perform the study on the data
    • On a next article we will see this analysis in detail!
@RestController
public class AnalysisController
{
@Autowired
private StokkerClient stokkerClient;
@Autowired
private AnalyzeService analyzeService;
@RequestMapping("/analyzeQuote")
public Long analyzeQuote(@Param("ticker") String ticker){
// The discovery of the service and request handling is totally transparent to us!
List<StockQuotation> quotations = stokkerClient.getAllStockQuotations(ticker);
// Once we get the data, we request a new analysis
return analyzeService.analyzeStockQuotations(quotations);
}
}

Note: In case that the REST service you are consuming is following the HATEOAS approach, you will need to change the signature of your interface´s method:
/* Resources represent the link structure used in HATEOAS
*/
@RequestMapping(method = RequestMethod.GET, value = "/stockQuotationJPAs/search/findValueByStock")
Resources<StockQuotation> getAllStockQuotations(@RequestParam("ticker") String stock);
// In the client side you can access to the payload like this:
Resources<StockQuotation> quotations = stokkerClient.getAllStockQuotations(ticker);
List<StockQuotation> stockList = new ArrayList<>(quotations.getContent());
// But also you can access the link structure and keep consuming related services!

And don´t forget to add the feign dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
view raw Pom.xml hosted with ❤ by GitHub

Finally, annotate your Spring Boot application with this:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SparkkerApplication {
public static void main(String[] args) {
SpringApplication.run(SparkkerApplication.class, args);
}
}
view raw Sparkker.java hosted with ❤ by GitHub

Summary


We have quickly dispatched all the boilerplate and now we have our new data analysis module decoupled from everything else (perhaps another machine or even a cluster - if we late use Yarn, for instance -).
Eureka and Feign have greatly eased this task, working without you even noticing it. This is great and, its even greater if we think that these components were born as proprietary components that were later on made open source for all of us to use them!

Source 

You can find the source for all the applications in these GitHub repositories (feel free to contribute!):

No comments:

Post a Comment