SpringCloud --- 服务降级 ( Hystrix熔断器 )

Posted 黑桃️

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud --- 服务降级 ( Hystrix熔断器 )相关的知识,希望对你有一定的参考价值。

服务降级


Hystrix熔断器

🔺 概述

(1) 分布式系统面临的问题

复杂分布式体系结构中的应用程序 有数10个依赖关系,每个依赖关系在某些时候将不可避免地失败

(2) 是什么

(3) 能干嘛

  • 服务降级
  • 服务熔断
  • 接近实时的监控

(4) 官网资料

https://github.com/Netflix/hystrix/wiki

(5) Hystrix官宣,停更进维

  • 被动修复bugs
  • 不再接受合并请求
  • 不再发布新版本

🔺 HyStrix重要概念

(1) 服务降级

服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback

产生的原因

  • 程序运行异常
  • 超时
  • 服务熔断触发服务降级
  • 线程池/信号量也会导致服务降级

(2) 服务熔断

类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示

服务的降级 -> 进而熔断 -> 恢复调用链路

(3) 服务限流

秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行


🔺 hystrix案例

(1) 构建

① 新建模块 cloud-provider-hystrix-payment8001

② 配置 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>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

    <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>$project.version</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

③ 配置 application.yml

server:
  port: 8001
spring:
  application:
    name: cloud-provider-hystrix-payment
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

④ 创建主启动类

@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 
    public static void main(String[] args) 
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    

⑤ Service:PaymentService

@Service
public class PaymentService 

    // 正常访问
    public String paymentInfo_OK(Integer id) 
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\\t" + "O(∩_∩)O哈哈~";
    

    // 超时访问
    public String paymentInfo_TimeOut(Integer id) 
        int timeNumber = 3;
        try 
            // 暂停3秒钟
            TimeUnit.SECONDS.sleep(timeNumber);
         catch (InterruptedException e) 
            e.printStackTrace();
        

        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\\t" + "O(∩_∩)O哈哈~  耗时(秒)" + timeNumber;
    

⑥ Controller:PaymentController

@RestController
@Slf4j
public class PaymentController 
    @Resource
    private PaymentService paymentService;

    @GetMapping("/payment/hystrix/ok/id")
    public String paymentInfo_OK(@PathVariable("id") Integer id) 
        String result = paymentService.paymentInfo_OK(id);
        log.info("**********result = " + result);
        return result;
    

    @GetMapping("/payment/hystrix/timeout/id")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) 
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*****result = " + result);
        return result;
    


⑦ 测试

以上述为根基平台,从正确->错误->降级熔断->恢复


(2) 高并发测试

① Jmeter压测测试

② Jmeter压测结论

上面还只是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖s

③ 看热闹不嫌弃事大,80新建加入

新建模块 cloud-consumer-feign-hystrix-order80

配置 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>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>$project.version</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
配置 application.yml
server:
  port: 80
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka


创建主启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderHystrixMain80 
    public static void main(String[] args) 
        SpringApplication.run(OrderHystrixMain80.class, args);
    

Service:PaymentHystrixService
@Component
@FeignClient("CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService 

    @GetMapping("/payment/hystrix/ok/id")
    public String paymentInfo_OK(@PathVariable("id") Integer id) ;

    @GetMapping("/payment/hystrix/timeout/id")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) ;


Controller:OrderHyrixController
@RestController
@Slf4j
public class OrderHyrixController 

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/payment/hystrix/ok/id")
    public String paymentInfo_OK(@PathVariable("id") Integer id) 
        return paymentHystrixService.paymentInfo_OK(id);
    
    
    @GetMapping("/payment/hystrix/timeout/id")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) 
        return paymentHystrixService.paymentInfo_TimeOut(id);
    

正常测试

高并发测试

服务端自测 8001时 200x100 调用,客户端通过 feign也是 200x100,相当于2万个线程去并发访问 8001

(3) 故障和导致现象

8001同一层次的其他接口被困死,因为tomcat线程池里面的工作线程已经被挤占完毕

80此时调用8001,客户端访问响应缓慢,转圈圈

(4) 上述结论

正因为有上述故障或不佳表现 才有我们的降级/容错/限流等技术诞生

(5) 如何解决?解决的要求

  • 超时导致服务器变慢(转圈) — 超时不再等待
  • 出错(宕机或程序运行出错) — 出错要有兜底

解决

  • 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
  • 对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
  • 对方服务(8001)ok,调用者(80)自己有故障或有自我要求(自己的等待时间小于服务提供者)

(6) 服务降级

① 降级配置

@HystrixCommand

② 8001 先从自身找问题

设置自身调用超时时间的峰值,峰值内可以正常运行, 超过了需要有兜底的方法处理,做服务降级fallback

③ 8001 fallback

主启动类加上注解才能生效:@EnableCircuitBreaker

对业务类 PaymentService 做些修改

// 超时访问
@HystrixCommand(fallbackMethod = "payment_TimeOutHandler",commandProperties = 
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="3000")
)
public String paymentInfo_TimeOut(Integer id) 
    int timeNumber = 13;
    try 
        // 暂停3秒钟
        TimeUnit.SECONDS.sleep(timeNumber);
     catch (InterruptedException e) 
        e.printStackTrace();
    

    return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\\t" + "O(∩_∩)O哈哈~  耗时(秒)" + timeNumber;


// 兜底的方案,paymentInfo_TimeOut出现超时或者异常的时候就会调用这个方法
public String payment_TimeOutHandler(Integer id) 
    return "/(ToT)/" + "调用支付接口超时或异常:\\t" + "\\t当前线程池名字" + Thread.currentThread().getName();

④ 80 fallback

在 pom.xml 加入依赖:
<!--hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在 application.yml 中加入配置:
#开启 feign 的 hystrix支持,默认是false
#个人

以上是关于SpringCloud --- 服务降级 ( Hystrix熔断器 )的主要内容,如果未能解决你的问题,请参考以下文章

什么是服务降级?springCloud如何实现?

4.SpringCloud -- 服务降级熔断 HystrixSentinel

SpringCloud--服务降级--Hystrix之服务降级支付

springcloud-gateway-源码之降级篇

重学SpringCloud系列七之服务熔断降级hystrix

SpringCloud集成Hystrix