Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 3522

A Guide to Spring Cloud Netflix – Hystrix

$
0
0

The Master Class of "Learn Spring Security" is out:

>> CHECK OUT THE COURSE

1. Overview

This tutorial will cover Spring Cloud Netflix Hystrix, a fault tolerance library. We will use the library and implement the Circuit Breaker enterprise pattern, which is describing a strategy against failure cascading at different levels in the service-layer.

In simpler terms: How to allow one service to continue functioning – when it calls external services which are failing?

The principle is analogous to electronics: Hystrix is watching methods for failing calls to related services. If there is such a failing method, it will open the circuit, which means, it forwards the call to a fallback method.

The library will tolerate failures up to a threshold. Beyond that, it leaves the circuit open. Which means, it will forward all subsequent calls to the fallback method, to prevent future fails. This creates a time buffer for the related service to recover from its failing state.

2. REST Producer

To create a scenario, which demonstrates the Circuit Breaker pattern, we need a ‘related’ service first. We’ll name it ‘REST Producer’, because it provides data for the Hystrix-enabled ‘REST Consumer’ – which we’ll create in the next step.

If you’re already familiar with the implementation of a REST service, or if you already have one ‘ready-to-go’, you can skip this section.

Otherwise let’s create a new Maven project using the spring-boot-starter-web dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.4.0.RELEASE</version>
</dependency>

The project itself is intentionally kept simple. It will consist of a controller interface with one @RequestMapper annotated GET method returning simply a String, a @RestController implementing this interface and a @SpringBootApplication.

We’ll begin with the interface:

public interface GreetingController {
    @RequestMapping("/greeting/{username}")
    String greeting(@PathVariable("username") String username);
}

And the implementation:

@RestController
public class GreetingControllerImpl implements GreetingController {
    @Override
    public String greeting(@PathVariable("username") String username) {
        return String.format("Hello %s!\n", username);
    }
}

Next we’ll write down the main application class:

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

To complete this section, the only thing left to do, is to configure an application-port on which we will be listening. We won’t use the default port 8080, because the port should remaining reserved for the application described in the next step.

Furthermore we’re defining an application name, to be able to look-up our ‘REST Producer’ from the client application that we’ll introduce, later. Let’s create an application.properties with the following content:

server.port=9090
spring.application.name=rest-producer

Now we’re able to test our ‘REST Producer’ using curl:

$> curl http://localhost:9090/greeting/Cid
Hello Cid!

3. REST Consumer with Hystrix

For our demonstration scenario, we’ll be implementing a web-application, which is consuming the REST service from the previous step using RestTemplate and Hystrix. For the sake of simplicity, we’ll call it the ‘REST Consumer’.

Consequently we create a new Maven project with spring-cloud-starter-hystrix, spring-boot-starter-web and spring-boot-starter-thymeleaf as dependencies:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.4.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>1.4.0.RELEASE</version>
</dependency>

For the Circuit Breaker to work, Hystix will scan @Component or @Service annotated classes for @HystixCommand annotated methods, implement a proxy for it and monitor its calls.

We’re going to create a @Service class first, which will be autowired into a @Controller. Since we’re building a web-application using Thymeleaf, we also need a HTML template as view.

This will be our inject-able @Service implementing a @HystrixCommand with an associated fallback method. This fallback has to use the same signature as the ‘original’:

@Service
public class GreetingService {
    @HystrixCommand(fallbackMethod = "defaultGreeting")
    public String getGreeting(String username) {
        return new RestTemplate()
          .getForObject("http://localhost:9090/greeting/{username}", 
          String.class, username);
    }

    private String defaultGreeting(String username) {
        return "Hello User!";
    }
}

RestConsumerApplication will be our main application class. The @EnableCircuitBreaker annotation will scan the classpath for any compatible Circuit Breaker implementation.

To use Hystrix explicitly you have to annotate this class with @EnableHystrix:

@SpringBootApplication
@EnableCircuitBreaker
public class RestConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RestConsumerApplication.class, args);
    }
}

We’ll set up the controller using our GreetingService:

@Controller
public class GreetingController {
    @Autowired
    private GreetingService greetingService;

    @RequestMapping("/get-greeting/{username}")
    public String getGreeting(Model model, @PathVariable("username") String username) {
        model.addAttribute("greeting", greetingService.getGreeting(username));
        return "greeting-view";
    }
}

And here’s the HTML template:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Greetings from Hystrix</title>
    </head>
    <body>
        <h2 th:text="${greeting}"/>
    </body>
</html>

To ensure that the application is listening at a defined port, we put the following in an application.properties file:

server.port=8080

To see a Hystix circuit breaker in action, we’re starting our ‘REST Consumer’ and pointing our browser to http://localhost:8080/get-greeting/Cid. Under normal circumstances, the following will be shown:

Hello Cid!

To simulate a failure of our ‘REST Producer’, we’ll simply stop it and after we finished refreshing the browser we should see a generic message, returned from the fallback method in our @Service:

Hello User!

4. REST Consumer with Hystrix and Feign

Now we’re going to modify the project from the previous step to use Spring Netflix Feign as declarative REST client, instead of Spring RestTemplate.

The advantage is, that we’re later able to easily refactor our Feign Client interface to use Spring Netflix Eureka for service discovery.

To start the new project, we’ll make a copy of our ‘REST Consumer’, and add the ‘REST Producer’ and spring-cloud-starter-feign as dependency to the pom.xml:

<dependency>
    <groupId>com.baeldung.spring.cloud</groupId>
    <artifactId>spring-cloud-hystrix-rest-producer</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>

Now we’re able to use our GreetingController to extend a Feign Client. We will implement Hystrix fallback as an inner static class annotated with @Component.

Alternatively, we could define a @Bean annotated method returning an instance of this fallback class.

The name property of the @FeignClient is mandatory. It is used, to look-up the application either by service discovery via an Eureka Client or via url, if this property is given. For more on using Spring Netflix Eureka for service discovery have a look at this article:

@FeignClient(
  name = "rest-producer"
  url = "http://localhost:9090", 
  fallback = GreetingClient.GreetingClientFallback.class
)
public interface GreetingClient extends GreetingController {
    
    @Component
    public static class GreetingClientFallback implements GreetingController {
        @Override
        public String greeting(@PathVariable("username") String username) {
            return "Hello User!";
        }
    }
}

In the RestConsumerFeignApplication, we’ll put an additional annotation to enable Feign integration, in fact @EnableFeignClients, to the application main class:

@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients
public class RestConsumerFeignApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(RestConsumerFeignApplication.class, args);
    }
}

We’re going to modify the controller to use an auto-wired Feign Client, rather than the previously injected @Service, to retrieve our greeting:

@Controller
public class GreetingController {
    @Autowired
    private GreetingClient greetingClient;

    @RequestMapping("/get-greeting/{username}")
    public String getGreeting(Model model, @PathVariable("username") String username) {
        model.addAttribute("greeting", greetingClient.greeting(username));
        return "greeting-view";
    }
}

To distinct this example from the previous, we will alter the application listening port in the application.properties:

server.port=8082

Finally we will test this Feign-enabled ‘REST Consumer’ like the one from the previous section. The expected result should be the same.

5. Using Scopes

Normally a @HytrixCommand annotated method is executed in a thread pool context. But sometimes it needs to be running in a local scope, for example a @SessionScope or a @RequestScope. This can be done via giving arguments to the command annotation:

@HystrixCommand(fallbackMethod = "getSomeDefault", commandProperties = {
  @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
})

6. The Hystrix Dashboard

An optional nice feature of Hystrix is the ability to monitor its status on a dashboard.

To enable it, we’ll put spring-cloud-starter-hystrix-dashboard and spring-boot-starter-actuator in the pom.xml of our ‘REST Consumer’:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>1.4.0.RELEASE</version>
</dependency>

The former needs to be enabled via annotating a @Configuration with @EnableHystrixDashboard and the latter automatically enables the required metrics within our web-application.

After we’ve done restarting the application, we’ll point a browser at http://localhost:8080/hystrix, input the metrics url of a ‘hystrix.stream’ and begin monitoring. Finally we should see something like this:

Picture of an example Hytrix Dashboard

Monitoring a ‘hystrix.stream’ is something fine, but if you have to watch multiple Hystrix-enabled applications, it will become inconvenient. For this purpose Spring Cloud provides a tool called Turbine, which is able to aggregate streams to present in one Hystrix Dashboard.

Configuring Turbine is beyond the scope of this write-up, but the possibility should be mentioned here. So its also possible to collect these streams via messaging, using Turbine Stream.

7. Conclusion

As we’ve seen so far, we’re now able to implement the Circuit Breaker pattern using Spring Netflix Hystrix together with either Spring RestTemplate or Spring Netflix Feign.

This means that we’re able to consume services with included fallback using ‘static’ or rather ‘default’ data and we’re able to monitor the usage of this data.

As usual, you’ll find the sources on GitHub.

The Master Class of "Learn Spring Security" is out:

>> CHECK OUT THE COURSE


Viewing all articles
Browse latest Browse all 3522

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>