SpringCloud-Hystrix

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