segunda-feira, 26 de setembro de 2016

Spring Cloud Netflix - Load Balancer with Ribbon/Feign

Spring Cloud Netflix to provide client-side load balancing in calls to another microservice.
The idea in this post is show some concepts about load balancing, Ribbon and Feign, and step by step who work Ribbon and Feign.




Load Balancing 

 Load Balancing automatically distributes incoming application traffic between two or more computers. It enables you to achieve fault tolerance in your applications, seamlessly providing the required amount of load balancing capacity needed to route application traffic. Load balancing aims to optimize resource use, maximize throughput, minimize response time, and avoid overload of any single resource. Using multiple components with load balancing instead of a single component may increase reliability and availability through redundancy.
Ribbon  - Load Balancer without Eureka 

Ribbon is a client side load balancer, which gives you a lot of control over the behavior of HTTP and TCP clients. Ribbon's Client component offers a good set of configuration options such as connection timeouts, retries, retry algorithm (exponential, bounded back off) etc. Ribbon comes built in with a pluggable and customizable Load Balancing component. 
Some of the load balancing strategies offered are listed below:

  • Simple Round Robin LB
  • Weighted Response Time LB
  • Zone Aware Round Robin LB
  • Random LB


Client-Side  Load Balancing

An approach to load balancing is to deliver a list of server IPs to the client, and then to have client randomly select the IP from the list on each connection. It has been claimed that client-side random load balancing tends to provide better load distribution than round-robin DNS. With this approach, the method of delivery of list of IPs to the client can vary, and may be implemented as a DNS list (delivered to all the clients without any round-robin), or via hardcoding it to the list. If a "smart client" is used, detecting that randomly selected server is down and connecting randomly again, it also provides fault tolerance.
Ribbon provides the following features:

  • Load balancing
  • Fault tolerance
  • Multiple protocol (HTTP, TCP, UDP) support in an asynchronous and reactive model
  • Caching and batching
A central concept in Ribbon is that of the named client. Each load balancer is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer.

Using Ribbon - Code:


First Service is hello-service 

This service provide some aleatory greeting when access http://localhost:8090/greeting


@RestController
@SpringBootApplication
public class HelloApplication {

private static Logger log = LoggerFactory.getLogger(HelloApplication.class);

@RequestMapping(value = "/greeting")
public String greet() {
log.info("Access /greeting");

List<String> greetings = Arrays.asList("Hi there", "Greetings", "Salutations");
Random rand = new Random();

int randomNum = rand.nextInt(greetings.size());
return greetings.get(randomNum);
}

@RequestMapping(value = "/")
public String home() {
log.info("Access /");
return "Hi!";
}

public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}

}


File: application.yml

spring:
  application:
    name: hello-service

server:
  port: 8090

OBS: should create two more hello-service in Port 9092, 9999


Second Service is user-service 
This service call for hello-service, operation greeting, with ribbon configured, this service call hello-service based on algorithm round-robin.


@SpringBootApplication
@RestController
@RibbonClient(name = "hello-service", configuration = HelloServiceConfiguration.class)
public class UserApplication {

@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}

@Autowired
RestTemplate restTemplate;

@RequestMapping("/hi")
public String hi(@RequestParam(value = "name", defaultValue = "Rafael") String name) {
String greeting = this.restTemplate.getForObject("http://hello-service/greeting", String.class);
return String.format("%s, %s!", greeting, name);
}

public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}

}

public class HelloServiceConfiguration {
@Autowired
IClientConfig ribbonClientConfig;

@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}

@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}

File: application.yml
In ribbon configuration are listed all services available.


spring:
  application:
    name: user-service

server:
  port: 8888
  
hello-service:
  ribbon:
    eureka:
      enabled: false
    listOfServers: localhost:8090,localhost:9092,localhost:9999

    ServerListRefreshInterval: 15000


When we call the service on URL http://localhost:8090/greeting , on console is possible see each service it is called, for example, in the fist call any host configured on list of servers could be used.



Feign - Load Balancer using Eureka 

Feign is a declarative web service client, or declarative REST client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. 
Spring Cloud integrates Ribbon and Eureka to provide a load balanced http client when using Feign.

I talk more about Eureka here.

Using Feign - Code:

Eureka server Project: http://localhost:8761


@SpringBootApplication
@EnableEurekaServer
public class ApplicationEurekaServer {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ApplicationEurekaServer.class)
        .web(true)
        .run(args);
    }

}

File: application.yml

#Server Specifics
server:
  port: 8761

error:
    whitelabel:
      enabled: false

spring:
  application:
    name: eureka-server

#Eureka Specifics

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/



Service is hello-service http://localhost:7111/greeting



@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class HelloApplication {
@Autowired
DiscoveryClient client;

@RequestMapping("/")
public String hello() {
ServiceInstance localInstance = client.getLocalServiceInstance();
return "Hello World: "+ localInstance.getServiceId()+":"+localInstance.getHost()+":"+localInstance.getPort();
}

public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}

}

File: application.yml

spring:
  application:
    name: hello-service

server:
  port: 7111
  
eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/  

Should be create other hello-service on Port 7112 http://localhost:7112/greeting

And the service that will consume these hello-service is user-service.


@SpringBootApplication
@EnableDiscoveryClient
@RestController
@EnableFeignClients

public class UserApplication {
@Autowired
HelloClient client;

@RequestMapping("/")
public String hello() {
return client.hello();
}

public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}

@FeignClient("hello-service")
interface HelloClient {
@RequestMapping(value = "/", method = GET)
String hello();
}

}

File: application.yml

spring:
  application:
    name: user-service

server:
  port: 7211
  
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    leaseRenewalIntervalInSeconds: 10
    metadataMap:
      instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${server.port}}}

endpoints:
  restart:
    enabled: true
  shutdown:
    enabled: true
  health:
    sensitive: false



This service will be available on http://localhost:7211
user-service will ask to Eureka the hello-service address, and will receive some address that is registered.

If everything OK this service will be registered on Eureka:


When we call http://localhost:7211 on of these message will be showed:

Hello World: hello-service:192.168.0.14:7112

Hello World: hello-service:192.168.0.14:7111



References:


terça-feira, 6 de setembro de 2016

Spring Cloud Netflix - How works Circuit Breaker - Eureka,Hystrix and Turbine

As for working with micro-service remote calls for executions in different software is the most common thing in an environment.
Probably these systems will be on different machines distributed over a network.
But what happens when a call to one of these systems fails or has an answer in an inappropriate time? The system may failures as a whole and the problem can be cascaded to other systems that depend on this request.
To avoid this kind of problem a software pattern called Circuit Breaker.
The idea is simple, exactly how a circuit breaker of a house.
You design a remote call for a circuit breaker object, which monitors failures.

When these failures arrive at a certain limit, the circuit breaker trips, and when he disarms can be put an action in case of failure or even a warning that the circuit breaker tripped.



The Netflix has created a tool that implements Circuit Breaker call Hystrix and Spring Cloud eased further to implement this standard. 

The first component is eureka : 


To configure the first component for this sample is eureka server.




And this component should be the first to run, when up this screen is available on http://localhost:8761


The second component is service with eureka client called recommend-service: 



This service has two operations that provide book name, when buy another to recommend.

Available on http://localhost:8090




The third component is the Service with Hystrix/ Circuit Breaker.

This service is called read-service, in this service will be check if service dependency (recommended-service) is available, and check if circuit is Open or Close.

There are two operations: /to-read  and /to-buy

This service has configuration to Hystrix Dashboard and eureka client as well.

Available on http://localhost:8080




In this picture the annotation @HystrixCommand is the configuration to circuit breaker, if something wrong happen the method reliable will be called, the same in the second @HystrixCommand, if something wrong happen the method realiableBuy will be called.
The services are called through Eureka Client, the concept is, ask for eureka server the address to recommend-service for example.



The last component is Turbine is a tool for aggregate events produced on Hystrix.

Lets imagine that we have a cluster with almost 10 read-service, each one with Hystrix, is difficult monitoring all circuits. In this moment Turbine works, to provide aggregation to circuits breakers.

Available on http://localhost:8989


In this picture the cluster config here mean the service that has @HystrixCommand, we should put the service name registered on Eureka = READ


After all this components started on Eureka server  http://localhost:8761 is possible see 3 instances registered.



In this link http://localhost:8080/hystrix will be available the hyxtrix dashboard.
Inform the Single Hystrix http://localhost:8080/hystrix.stream


This screen below is waiting for request to build the circuit



In this picture Circuit is closed.


To open Turbine is the same step that Hystrix, but should inform the Cluster via Turbine:http://localhost:8989//turbine.stream?cluster=READ

Will open the same screen that Hystrix, but if I have more service, will appear in aggregate way.


Now I switched off the recommend-service.

The default configuration is 20 request to consider circuit open, I did 20 time this request http://localhost:8080/to-buy
The result: