In microservices, we split the system into many service units, and each unit is interdependent through service registration and subscription consumption. But what happens if some services have problems?
For example, there are three services (ABC), A calls B and B calls C. Due to network delay or C's code problems, B will not get a response for a long time, so the request B calls C will be suspended and waiting.
In the case of high concurrent access, these suspended threads cannot be released, causing subsequent requests to block, and eventually B will also hang up. By analogy, A may also hang up, causing the entire system to crash.
In order to solve the entire problem, Spring Cloud uses Hystrix for service fault-tolerant protection, including a series of protection functions such as circuit breakers and thread isolation. Today we will take a look at how to implement circuit breakers through Hystrix.
1. What is Spring Cloud Hystrix? What is a circuit breaker?
Spring Cloud Hystrix is implemented based on Netflix's open source framework, Hystrix, and its purpose is to provide strong fault tolerance for latency and failure by controlling those nodes that access remote systems, services and third parties.
The circuit breaker is similar to the leakage circuit breaker used in the strong electric box in our home. When the service unit fails (similar to the short circuit of the electrical appliance), an error response is returned to the caller through the fault monitoring function of the circuit breaker (similar to the fuse), avoiding long-term waiting, thereby preventing the fault from spreading to the entire system.
2. If there is no circuit breaker, the page display
Do you still remember the three services in the spring cloud introduction series 2: using Eureka for service governance (eureka/hello-service/hello-consumer) we wrote earlier? We conduct experiments based on this.
1. Start the eureka service registration center with port number 1111
2. Start the hello-service service provider. Here we start two services, with port numbers 9090,9091 respectively.
3. Start hello-consumer to serve consumers, with the port number 9999; at this time, we have no problem visiting http://localhost:9999/hello-consumer several times.
4. Turn off the service with port number 9091, and then visit http://localhost:9999/hello-consumer several times, and an error was reported.
PS: Here we explain why we need to access multiple times, because we have achieved load balancing through ribbon. When we access http://localhost:9999/hello-consumer, we will poll two services that access hello-service. An error will be reported when accessing a service with port number 9091. There will be no problem with accessing a service with 9090.
3. Circuit breaker code implementation
Next, let’s take a look at how to implement the code. We do not modify the service registration center and service provider, we only need to modify the service consumer hello-consumer.
1. Modify POM files and introduce Hystrix dependencies
<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"> <modelVersion>4.0.0</modelVersion> <groupId>com.sam</groupId> <artifactId>hello-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <javaVersion>1.8</javaVersion> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependency> </dependencyManagement> <dependencies> <!-- Introduce eureka client dependencies--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- Introduce ribbon Dependency is used to implement load balancing. We are just using it here and will not introduce it elsewhere --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <!-- Introducing hystrix dependencies to implement service fault tolerance protection--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> </dependencies></project>
2. Modify the startup class, add annotation @EnableCircuitBreaker, and enable the circuit breaker
@EnableDiscoveryClient@SpringBootApplication@EnableCircuitBreakerpublic class ConsumerApp { //@Bean is applied on the method and used to set the method return value to be bean @Bean @LoadBalanced //@LoadBalanced to achieve load balancing public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); }}At this time, you will find that this startup class has three annotations. Isn't this very troublesome? It doesn't matter, we can use the annotation @SpringCloudApplication
@SpringCloudApplicationpublic class ConsumerApp { //@Bean is applied on the method and is used to set the method return value to be bean @Bean @LoadBalanced //@LoadBalanced to achieve load balancing public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); }}@SpringCloudApplication = @EnableDiscoveryClient +@SpringBootApplication +@EnableCircuitBreaker, you can see from the source code:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootApplication@EnableDiscoveryClient@EnableCircuitBreakerpublic @interface SpringCloudApplication {}3.Add service
@Servicepublic class ConsumerService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "errorMsg") public String consumer() { // Call the hello-service service, note that the service name is used here, not the specific ip+port restTemplate.getForObject("http://hello-service/hello", String.class); return "hello consumer finish !!!"; } public String errorMsg() { return "error!!!"; }}We put the original controller's implementation of calling RestTemplate into the service, and specify the callback method through @HystrixCommand, and call this method when an error occurs.
4. Modify the controller
/** *The restTemplate is no longer called directly here, *but is implemented by calling service* */@RestControllerpublic class ConsumerController { @Autowired// RestTemplate restTemplate; ConsumerService service; @RequestMapping("/hello-consumer") public String helloConsumer() {// // Call the hello-service service, note that the service name is used here, not the specific ip+port// restTemplate.getForObject("http://hello-service/hello", String.class); return service.consumer(); }}5. Test, multiple visits, and when an error is reported, the following content will be displayed.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.