Introduction
Spring cloud provides Hystrix fault-tolerant library to implement a downgrade strategy for methods configured with circuit breakers when the service is unavailable and temporarily call alternate methods. This article will create a product microservice, register with the eureka service registration center, and then we use the web client access/products API to get the product list, and when the product service fails, the local standby method is called to downgrade but provide the service normally.
Basic environment
Git: Project source code
Add product services
Create a new maven project in intelliJ, using the following configuration
Then add the following code in pom.xml:
<?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"> <modelVersion>4.0.0</modelVersion> <groupId>cn.zxuqian</groupId> <artifactId>productService</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <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-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependency> </dependency> <dependencyManagement> <dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M9</version> <type>pom</type> <scope>import</scope> </dependency> </dependency> </dependency> </dependencyManagement> <build> <plugins> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repository> </repositories></project>
We continued to use spring-cloud-starter-netflix-eureka-client to enable product and service to be automatically registered with eureka services. Then spring-cloud-starter-config is also used to read the configuration file of the configuration service center. This project is just a simple spring web project.
Create a bootstrap.yml file under src/main/resources and add the following content:
spring: application: name: product-service cloud: config: uri: http://localhost:8888
Create the product-service.yml file in the git repository in the configuration center and add the following configuration and submit:
server: port: 8081
This configuration specifies that the product service port is 8081. Then create the Application class and add the following code:
package cn.zxuqian;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}The @EnableDiscoveryClient annotation will instruct spring cloud to automatically register this service with eureka. Finally, create the cn.zxuqian.controllers.ProductController controller, provide the /products API, and return the sample data:
package cn.zxuqian.controllers;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class ProductController { @RequestMapping("/products") public String productList() { return "Coat, Jacket, Sweater, T-shirt"; }}Configure the web client
Open the web project we created earlier and add a new Hystrix dependency to pom.xml:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>
Then update the code of the Application class:
package cn.zxuqian;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.client.RestTemplateBuilder;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.context.annotation.Bean;import org.springframework.web.client.RestTemplate;@EnableCircuitBreaker@EnableDiscoveryClient@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public RestTemplate rest(RestTemplateBuilder builder) { return builder.build(); }}Here, @EnableCircuitBreaker is used to enable the circuit breaker function, and then a rest method is added and the @Bean annotation is used. This part belongs to the Spring dependency injection function. The method using the @Bean tag will tell you how to initialize such objects. For example, in this example, using RestTemplateBuilder to create a RestTemplate object, which will be used later in the service using a circuit breaker.
Create the cn.zxuqian.service.ProductService class and add the following code:
package cn.zxuqian.services;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.stereotype.Service;import org.springframework.web.client.RestTemplate;import java.util.List;@Servicepublic class ProductService { private final RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; public ProductService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @HystrixCommand(fallbackMethod = "backupProductList") public String productList() { List<ServiceInstance> instances = this.discoveryClient.getInstances("product-service"); if(instances != null && instances.size() > 0) { return this.restTemplate.getForObject(instances.get(0).getUri() + "/products", String.class); } return ""; } public String backupProductList() { return "Jack, Sweater"; }} The reason for creating a Service class is that Hystrix can only be used in classes marked @Service or @Component, so that the API provided by Spring Context can be used normally. This will be explained later when you go deep into Spring.
After using @HystrixCommand annotation, Hystrix will monitor the annotated method, namely productList (the underlying layer wraps this method to achieve monitoring). Once the errors of this method accumulate to a certain threshold, the circuit breaker will be started. All subsequent requests to call the productList method will fail, and the method specified by fallbackMethod will be temporarily called backupProductList(), and then when the service returns to normal, the circuit breaker will be closed.
In this class, we also use DiscoveryClient to find the uri address of the product service, use the value of the spring.application.name configuration item of the product service, that is, the product-service is passed to the discoveryClient.getInstances() method as the serviceID, and then a list will be returned. Because we only have one product service started, we only need to take the uri address of the first instance.
Then we use RestTemplate to access the API of the product service. Note that Spring's constructor injection is used here, that is, the method we annotated with @Bean will be used to initialize the restTemplate variable without us manually initializing it. The RestTemplate class provides the getForObject() method to access other Rest APIs and wrap the results into an object form. The first parameter is the uri address of the API to be accessed, and the second parameter is the type of the result obtained. Here we return String, so we pass it to String.class.
The backupProductList() method returns the downgraded product list information.
Finally, create a controller cn.zxuqian.controllers.ProductController and add the following code:
package cn.zxuqian.controllers;import cn.zxuqian.services.ProductService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class ProductController { @Autowired private ProductService productService; @RequestMapping("/products") public String productList() { return productService.productList(); }} Here, use ProductService to provide data for the /products path.
test
First, we use the spring-boot:run plug-in to start the configuration center service, config-server, then start eureka-server, then start product-service, and finally start the web client. Wait for a while before the eureka service is registered successfully and visit http://localhost:8080/products. Under normal circumstances, we will get the result of jacket, jacket, sweater, and T-shirt. Then we close product-service, and then access the same path, and we will get the result of downgrading: jacket, sweater
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.