1. Introduction
(1) Avoid the failure of a single service to cause the entire application to collapse.
(2) Service degradation: When the service times out, the service is abnormal, or the service is down, the defined method is executed. (Do something else)
(3) Service circuit breaker: When the circuit breaker condition is reached, the service is prohibited from being accessed and the defined method is executed. (no more)
(4) Service current limiting: When a large wave of traffic comes in in a high-concurrency scenario, let it queue up for access. (do it in line)
2. Build the project
(1)pom.xml
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.wsh.springcloud</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
(2) Write application.yml file
server: port: 8001 spring: application: name: cloud-provider-hystrix-payment datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true & amp;characterEncoding=utf-8 & amp;useSSL=false username: root password: wsh666 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka1.com:7001/eureka instance: instance-id: hystrixPayment8001 prefer-ip-address: true
(3) Start class PaymentHystrixMain8001
package com.wsh.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** * @ClassName PaymentHystrixMain8001 * @Description: TODO * @Author wshaha * @Date 2023/10/14 * @Version V1.0 **/ @SpringBootApplication @EnableEurekaClient public class PaymentHystrixMain8001 {<!-- --> public static void main(String[] args) {<!-- --> SpringApplication.run(PaymentHystrixMain8001.class, args); } }
(4)Write PaymentService
package com.wsh.springcloud.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * @ClassName PaymentService * @Description: TODO * @Author wshaha * @Date 2023/10/14 * @Version V1.0 **/ @Service @Slf4j public class PaymentService {<!-- --> public String test1(Integer id){<!-- --> return "test1: " + Thread.currentThread().getName() + " " + id; } public String test2(Integer id){<!-- --> try {<!-- --> Thread.sleep(3000); } catch (Exception e){<!-- --> } return "test2: " + Thread.currentThread().getName() + " " + id; } }
(5)Write PaymentController
package com.wsh.springcloud.controller; import com.wsh.springcloud.service.PaymentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @ClassName PaymentController * @Description: TODO * @Author wshaha * @Date 2023/10/14 * @Version V1.0 **/ @Slf4j @RequestMapping("/payment") @RestController public class PaymentController {<!-- --> @Autowired private PaymentService paymentService; @GetMapping("/test1/{id}") public String test1(@PathVariable("id") Integer id){<!-- --> return paymentService.test1(id); } @GetMapping("/test2/{id}") public String test2(@PathVariable("id") Integer id){<!-- --> return paymentService.test2(id); } }
(6) Run
3. Service downgrade configuration
(1) Method 1 (Once an exception occurs or the method times out, the downgrade method of @HystrixCommand will be called)
Note: @EnableCircuitBreaker is used to start the circuit breaker and supports a variety of circuit breakers. @EnableHystrix is used to start the circuit breaker and only supports Hystrix circuit breakers.
a. Write a downgrade method for PaymentService and configure the annotation @HystrixCommand
@Service @Slf4j public class PaymentService {<!-- --> public String test1(Integer id){<!-- --> return "test1: " + Thread.currentThread().getName() + " " + id; } @HystrixCommand(fallbackMethod = "test2fallback", commandProperties = {<!-- --> @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") }) public String test2(Integer id){<!-- --> try {<!-- --> Thread.sleep(3000); } catch (Exception e){<!-- --> } return "test2: " + Thread.currentThread().getName() + " " + id; } public String test2fallback(Integer id){<!-- --> return "test2fallback: " + id; } }
b. Start-up type turns on the circuit breaker
@SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class PaymentHystrixMain8001 {<!-- --> public static void main(String[] args) {<!-- --> SpringApplication.run(PaymentHystrixMain8001.class, args); } }
c. Run
(2) Method 2 (Configure the downgrade method for global use)
a. Use @DefaultProperties and note that the parameter list of the global downgrade method is empty.
@Slf4j @RequestMapping("/payment") @RestController @DefaultProperties(defaultFallback = "defaultFallbacktest") public class PaymentController {<!-- --> @Autowired private PaymentService paymentService; @GetMapping("/test1/{id}") public String test1(@PathVariable("id") Integer id){<!-- --> return paymentService.test1(id); } @GetMapping("/test2/{id}") @HystrixCommand public String test2(@PathVariable("id") Integer id){<!-- --> return paymentService.test2(id); } public String defaultFallbacktest(){<!-- --> return "defaultFallbacktest"; } }
b. Write PaymentService
@Service @Slf4j public class PaymentService {<!-- --> public String test1(Integer id){<!-- --> return "test1: " + Thread.currentThread().getName() + " " + id; } // @HystrixCommand(fallbackMethod = "test2fallback", commandProperties = {<!-- --> // @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") // }) public String test2(Integer id){<!-- --> // try {<!-- --> // Thread.sleep(3000); // } catch (Exception e){<!-- --> // // } int i = 1 / 0; return "test2: " + Thread.currentThread().getName() + " " + id; } // public String test2fallback(Integer id){<!-- --> // return "test2fallback: " + id; // } }
c. Run
(3) Method 3: When feign calls other services, you can use the corresponding downgrade method of the implementation class
a. Add dependencies to pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
b. application.yml added
feign: hystrix: enabled: true
c. Write the remote calling interface FeignTestService
@Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE", fallback = FeignTestServiceImpl.class) public interface FeignTestService {<!-- --> @GetMapping("/payment/test") public CommonResult<String> test(); }
d. Write the downgrade method class FeignTestServiceImpl
@Component public class FeignTestServiceImpl implements FeignTestService{<!-- --> @Override public CommonResult<String> test() {<!-- --> return new CommonResult(444, "", "error"); } }
e. Write Controller
@Slf4j @RequestMapping("/payment") @RestController //@DefaultProperties(defaultFallback = "defaultFallbacktest") public class PaymentController {<!-- --> @Autowired private PaymentService paymentService; @Autowired private FeignTestService feignTestService; @GetMapping("/test1/{id}") public String test1(@PathVariable("id") Integer id){<!-- --> return paymentService.test1(id); } @GetMapping("/test2/{id}") // @HystrixCommand public String test2(@PathVariable("id") Integer id){<!-- --> return paymentService.test2(id); } // public String defaultFallbacktest(){<!-- --> // return "defaultFallbacktest"; // } @GetMapping("/test") public CommonResult<String> test(){<!-- --> return feignTestService.test(); } }
f. Run
4. Service circuit breaker configuration
(1) The service is first downgraded, then disconnected, and then attempted to be restored.
(2) Within the specified time, the number of interface visits exceeds the set threshold, and the failure rate is greater than or equal to the set threshold.
(3) Example
@Service @Slf4j public class PaymentService {<!-- --> @HystrixCommand(fallbackMethod = "test1fallback", commandProperties = {<!-- --> @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//Turn on the circuit breaker @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//Threshold of request times @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//within the specified time @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//Failure rate threshold }) public String test1(Integer id){<!-- --> int i = 1 / id; return "test1: " + Thread.currentThread().getName() + " " + id; } public String test1fallback(Integer id){<!-- --> return "test1fallback: " + id; } // @HystrixCommand(fallbackMethod = "test2fallback", commandProperties = {<!-- --> // @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") // }) public String test2(Integer id){<!-- --> // try {<!-- --> // Thread.sleep(3000); // } catch (Exception e){<!-- --> // // } int i = 1 / 0; return "test2: " + Thread.currentThread().getName() + " " + id; } // public String test2fallback(Integer id){<!-- --> // return "test2fallback: " + id; // } }
5. Monitoring interface construction (only the hystrix takeover method can be monitored)
(1) Write 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"> <parent> <artifactId>demo20220821</artifactId> <groupId>com.wsh.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>com.wsh.springcloud</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- <dependency>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-starter-actuator</artifactId>--> <!-- </dependency>--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
(2)Write application.yml
server: port: 9001 spring: application: name: cloud-hystrix-dashboard
(3) Write startup class
package com.wsh.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; /** * @ClassName HystrixDashboardMain9001 * @Description: TODO * @Author wshaha * @Date 2023/10/14 * @Version V1.0 **/ @SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardMain9001 {<!-- --> public static void main(String[] args) {<!-- --> SpringApplication.run(HystrixDashboardMain9001.class, args); } }
(4) Run
(5) Configure monitored items
a.pom.xml
Add probe spring-boot-starter-actuator
b. Startup class plus
package com.wsh.springcloud; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; /** * @ClassName PaymentHystrixMain8001 * @Description: TODO * @Author wshaha * @Date 2023/10/14 * @Version V1.0 **/ @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker @EnableFeignClients public class PaymentHystrixMain8001 {<!-- --> public static void main(String[] args) {<!-- --> SpringApplication.run(PaymentHystrixMain8001.class, args); } @Bean public ServletRegistrationBean getServlet(){<!-- --> HystrixMetricsStreamServlet hystrixMetricsStreamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(hystrixMetricsStreamServlet); servletRegistrationBean.setLoadOnStartup(1); servletRegistrationBean.addUrlMappings("/hystrix.stream"); servletRegistrationBean.setName("HystrixMetricsStreamServlet"); return servletRegistrationBean; } }
(6) Monitoring interface