In general terms, a circuit breaker is:
An automatic device for stopping the flow of current in an electric circuit as a safety measure.
The same concept is used for microservice development, known as the Circuit Breaker design pattern. It tracks the availability of external services such as Eureka Server, API services such as restaurant-service
, and so on, and prevents service consumers from performing any action on any service that is not available.
It is another important aspect of microservice architecture, a safety measure (failsafe mechanism) when the service does not respond to a call made by the service consumer – circuit breaker.
We'll use Netflix Hystrix as a circuit breaker. It calls the internal fallback method in the service consumer when failures occur (for example due to a communication error or timeout). It executes embedded within its consumer of service. In the next section, you will find the code that implements this feature.
Hystrix opens the circuit and fail-fast when the service fails to respond repeatedly, until the service is available again. You must be wondering, if Hystrix opens the circuit, then how does it know that the service is available? It exceptionally allows some requests to call the service.
There are three steps for implementing fallback methods:
@EnableCircuitBreaker
. For example, if a user service would like to get the restaurant details, where a user has reserved the table:@SpringBootApplication @EnableCircuitBreaker @ComponentScan({"com.packtpub.mmj.user.service", "com.packtpub.mmj.common"}) public class UsersApp {
fallbackMethod
, the @HystrixCommand
annotation is used:@HystrixCommand(fallbackMethod = "defaultRestaurant") public ResponseEntity<Restaurant> getRestaurantById(int restaurantId) { LOG.debug("Get Restaurant By Id with Hystrix protection"); URI uri = util.getServiceUrl("restaurant-service"); String url = uri.toString() + "/v1/restaurants/" + restaurantId; LOG.debug("Get Restaurant By Id URL: {}", url); ResponseEntity<Restaurant> response = restTemplate.getForEntity(url, Restaurant.class); LOG.debug("Get Restaurant By Id http-status: {}", response.getStatusCode()); LOG.debug("GET Restaurant body: {}", response.getBody()); Restaurant restaurant = response.getBody(); LOG.debug("Restaurant ID: {}", restaurant.getId()); return serviceHelper.createOkResponse(restaurant); }
public ResponseEntity<Restaurant> defaultRestaurant(int restaurantId) { LOG.warn("Fallback method for restaurant-service is being used."); return serviceHelper.createResponse(null, HttpStatus.BAD_GATEWAY); }
These steps should be enough to failsafe the service calls and return a more appropriate response to the service consumer.
Hystrix provides the dashboard with a web UI that provides nice graphics of circuit breakers:
Netflix Turbine is a web application that connects to the instances of your Hystrix applications in a cluster and aggregates information, which it does in real time (updated every 0.5 seconds). Turbine provides information using a stream that is known as a turbine stream.
If you combine Hystrix with Netflix Turbine, then you can get all the information from Eureka Server on the Hystrix dashboard. This gives you a landscape view of all the information about the circuit breakers.
To use Turbine with Hystrix, just type in the Turbine URL http://localhost:8989/turbine.stream
(port 8989
is configured for the Turbine server in application.yml
) in first textbox shown before, and click on Monitory Stream.
Netflix Hystrix and Turbine uses RabbitMQ, an open source message queuing software. RabbitMQ works on Advance Messaging Queue Protocol (AMQP). It is a software in which queues can be defined, where applications can establish a connection and transfer a message through it. A message can include any kind of information. A message can be stored in the RabbitMQ queue until a receiver application connects and receives the message (taking the message off the queue).
Hystrix uses RabbitMQ to send a metrics data feed to Turbine.
We'll add the new Maven dependency, dashboard-server
for Hystrix Server. Configuring and using the Hystrix Dashboard is pretty simple in Spring Cloud like others. You just need to follow these steps:
pom.xml
:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter- hystrix-dashboard</artifactId> </dependency>
@EnableHystrixDashboard
annotation in the main Java class does everything for you to use it. We'll also use the @Controller
to forward the request from the root to Hystrix, as shown here:@SpringBootApplication @Controller @EnableHystrixDashboard public class DashboardApp extends SpringBootServletInitializer { @RequestMapping("/") public String home() { return "forward:/hystrix"; } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(DashboardApp.class).web(true); } public static void main(String[] args) { SpringApplication.run(DashboardApp.class, args); } }
application.yml
, as shown here:application.yml # Hystrix Dashboard properties spring: application: name: dashboard-server endpoints: restart: enabled: true shutdown: enabled: true server: port: 7979 eureka: instance: leaseRenewalIntervalInSeconds: 3 metadataMap: instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}} client: # Default values comes from org.springframework.cloud.netflix.eurek.EurekaClientConfigBean registryFetchIntervalSeconds: 5 instanceInfoReplicationIntervalSeconds: 5 initialInstanceInfoReplicationIntervalSeconds: 5 serviceUrl: defaultZone: http://localhost:8761/eureka/ fetchRegistry: false logging: level: ROOT: WARN org.springframework.web: WARN
We'll create one more Maven dependency for Turbine. When you run the Hystrix Dashboard application, it will look like the Default Hystrix Dashboard screenshot shown earlier.
Now, we will configure the Turbine Server using the following steps:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine-amqp</artifactId> </dependency>
@EnableTurbineAmqp
annotation in your application class as shown here. We are also defining a bean that will return the RabbitMQ Connection Factory:@SpringBootApplication @EnableTurbineAmqp @EnableDiscoveryClient public class TurbineApp { private static final Logger LOG = LoggerFactory.getLogger(TurbineApp.class); @Value("${app.rabbitmq.host:localhost}") String rabbitMQHost; @Bean public ConnectionFactory connectionFactory() { LOG.info("Creating RabbitMQHost ConnectionFactory for host: {}", rabbitMQHost); CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(rabbitMQHost); return cachingConnectionFactory; } public static void main(String[] args) { SpringApplication.run(TurbineApp.class, args); } }
application.yml
, as shown here:server:port
: The main port used by the the turbine HTTP
management:port
: Port of turbine Actuator endpoints
application.yml spring: application: name: turbine-server server: port: 8989 management: port: 8990 PREFIX: endpoints: restart: enabled: true shutdown: enabled: true eureka: instance: leaseRenewalIntervalInSeconds: 10 client: registryFetchIntervalSeconds: 5 instanceInfoReplicationIntervalSeconds: 5 initialInstanceInfoReplicationIntervalSeconds: 5 serviceUrl: defaultZone: http://localhost:8761/eureka/ logging: level: root: WARN com.netflix.discovery: 'OFF'