Spring Cloud Microservices Beginner Tutorial (1): Microservices Introduction Spring Cloud Microservices Beginner Tutorial (2): Service Registration and Discovery — Eureka Spring Cloud Microservices Beginner Tutorial (3): Service Registration Spring Cloud Microservices Beginner Tutorial (4): Inter-service Invocation — FeignClient Spring Cloud Microservices Beginner Tutorial (5): Centralized Config — ConfigService Spring Cloud Microservices Beginner Tutorial (6): Spring Cloud BUS Message Bus for Dynamic Config Refresh Spring Cloud Microservices Beginner Tutorial (7): Spring Cloud Stream Message-driven Microservices Spring Cloud Microservices Beginner Tutorial (8): Spring Cloud Zuul API Gateway Dynamic Routing, Cookie Passing, and CORS Spring Cloud Microservices Beginner Tutorial (9): Zuul Gateway Integrating Swagger2 for Auto-generated RESTful API Docs Spring Cloud Microservices Beginner Tutorial (10): Spring Cloud Hystrix Circuit Breaking and Service Degradation Spring Cloud Microservices Beginner Tutorial (11): Spring Cloud Sleuth + Zipkin Service Tracing and Link Monitoring Spring Cloud Microservices Beginner Tutorial (12): Spring Cloud Docker Containerized Deployment
Code will be shared at https://github.com/NeilRen/SpringCloudDemo, with different branches for different chapters; the Master branch contains the final combined content. This chapter’s code is at: https://github.com/NeilRen/SpringCloudDemo/tree/feature/spring-cloud-hystrix
Original This article is original, author: Ren Fei. Please cite the author and source when reposting.
In the previous section we covered the API gateway, which lets multiple services be published uniformly through the gateway. Before publishing, we also need to understand a mechanism in microservices — circuit breaking and service degradation, called Hystrix in Spring Cloud. This section integrates Hystrix to implement circuit breaking and degradation. Hystrix has many features; we only cover the most commonly used circuit breaking and degradation mechanisms.
What Is Circuit Breaking
Let’s first get a general idea of what circuit breaking is. In a large system, services call and depend on each other. If service A needs to call service B, but service B’s failure or a network fault prevents service A from getting the data it wants from B, waiting indefinitely causes thread blocking and resource waiting, slowing down the entire system’s data flow, and may even cause service A to go down after B does. So a circuit-breaking mechanism is needed: when service B’s error rate exceeds a certain ratio (default 50%), the circuit breaker opens for a period (default 5 seconds) and stops sending requests to B; after the break time elapses it tries B again. Once the dependent downstream service is unavailable, the circuit breaker cuts the request chain to avoid sending a large number of invalid requests that hurt system throughput, and the circuit breaker can self-detect and recover. So when circuit breaking stops requests to B, who do we call instead? That’s where the degradation mechanism comes in.
What Is Service Degradation
When the service we depend on fails, the circuit-breaking mechanism triggers. At that point you need to pre-provide a handler as the degradation method, generally called fallback; the fallback return value is usually a default value or comes from cache. It tells subsequent requests that the service is unavailable. A classic example is Taobao’s flash sale telling you the server is busy — that is service degradation. This message does not come from the backend service; it is responded to by the frontend service.
Add the Dependency
We add the spring-cloud-starter-hystrix dependency to the POM. The code is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud</artifactId>
<groupId>net.renfei</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>net.renfei</groupId>
<artifactId>gateway</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
</project>
Refactoring the Service That Needs Circuit Breaking
Add the @EnableCircuitBreaker annotation to the application startup class of the module that needs circuit breaking. Based on previous sections, we should now have three annotations: @EnableEurekaClient, @SpringBootApplication, and @EnableCircuitBreaker. In fact, these three can be simplified into one: @SpringCloudApplication, which already includes all three, so we remove those three and just use @SpringCloudApplication.
On the class that needs circuit breaking and degradation, you can use the @DefaultProperties(defaultFallback = “defaultFallback”) annotation to define a default degradation handler; on a specific method you can use @HystrixCommand(fallbackMethod = “fallbackMethod”) to define a degradation handler that applies only to that method. To demonstrate how to customize the timeout, circuit-break window, error rate, etc., I also added a commandProperties configuration — this is optional; I added it only for demonstration. It is an array, so you can configure multiple @HystrixProperty entries; the keys inside can be found in com.netflix.hystrix.HystrixCommandProperties.
My example is written in the controller of democlient; you could also write the circuit breaking and degradation in the service layer. I defined a default handler and also specified a timeout. The code is as follows:
package net.renfei.democlient.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import net.renfei.apicenter.request.DemoRquest;
import net.renfei.apicenter.result.Result;
import net.renfei.democlient.client.DemoServiceClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@DefaultProperties(defaultFallback = "defaultFallback")
public class DemoClientController {
@Autowired
private DemoServiceClient demoServiceClient;
@GetMapping("/")
@HystrixCommand(
fallbackMethod = "fallbackMethod",
commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // Enable circuit breaking
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // Number of requests within 10 seconds before it takes effect
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "1000"), // Circuit-break window time
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), // Error threshold percentage
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") // Timeout
})
public String getDemoService() {
DemoRquest demoRquest = new DemoRquest();
demoRquest.setMsg("This DemoRquest.Msg From DemoClientController.");
Result result = demoServiceClient.sayMsg(demoRquest);
return "You're visiting DemoClient. Call DemoService:{" + result.getMessage() + "}";
}
public String fallbackMethod() {
return "The DemoService is having issues, please try again later";
}
public String defaultFallback() {
return "This is the default fallback method";
}
}
Zuul Gateway Timeout Configuration
The Zuul gateway has no Controller when forwarding, so how do we configure it? We configure it in the configuration file. For convenience of demonstration I write it directly in bootstrap.yml:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds : 5000
If you want to test our degradation mechanism, just make the method that provides the service in demoservice sleep for 5 seconds, and the degradation mechanism will trigger.
