服务降级(Hystrix断路器)

1、概述

①、分布式系统面临的问题

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

服务降级(Hystrix断路器)

就像图中所看到的那样,一个用户请求需要调用A服务,但是A服务也需要调用P服务,然后PHHI等等······如果一切顺利,则没有啥问题,但是如果I服务出现了超时,会出现什么问题呢?

②、服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。

③、什么是Hystrix

Hystrix是一个用于处理分布式系统的延迟容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

能干嘛?

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

④、官网

点击访问

很遗憾,这款优秀的开源库,也停更了,SpringCloud社区里面神仙打架,我们程序员遭殃

服务降级(Hystrix断路器)

官网推荐使用resilience4j,但是国内用的很少,貌似今日头条就在用,另外还有一部分就是自研的,剩下的都是用阿里的Sentinel,关于Sentinel后面会介绍,所谓天下作业一通抄,它也是借鉴了Hystrix,所以这里我们先了解Hystrix的优秀设计思想,之后再看Sentinel就会很轻松。

2、三个重要概念

①、服务降级Fallback

我相信大家肯定写过这样的代码

if(){

}else if(){

}else if(){

}else {
    //对方系统不可用的,你需要给我一个兜底的解决方法
}

大家也遇到过这样的情况,打10086的时候,一阵悦耳动听的音乐结束后,电话那头响起:坐席忙,继续等待请按一,不愿等待,请挂机。可一看到,当服务器忙的时候,为了不让客户端长时间等待,应该立刻返回一个友好提示。fallback

哪些情况会出发降级?

  • 程序运行异常
  • 超时
  • 服务熔断出发服务降级
  • 线程池/信号量已满

②、服务熔断Break

类似保险丝,达到一些限制条件时,直接跳闸不允许请求,过程就是:服务的降级,进而熔断,再是恢复链路

③、服务限流Flowlimit

秒杀高并发等操作,严禁一窝蜂过来拥挤,等待排队

3、案例演示

①、构建支付微服务

Ⅰ、建module

服务降级(Hystrix断路器)

Ⅱ、POM

<?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>SpringCloudDemo</artifactId>
        <groupId>com.phz.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>HystrixProviderPayment8009</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.phz.springcloud</groupId>
            <artifactId>CloudAPI</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>
        <!-- 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-client</artifactId>
        </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>

Ⅲ、YML

server:
  port: 8009

spring:
  application:
    name: hystrix-payment-service

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
  #血和泪的教训,一定要配!
  instance:
    prefer-ip-address: true
    instance-id: payment8009

Ⅳ、主启动

/**
 * @author PengHuAnZhi
 * @createTime 2021/2/11 16:14
 * @projectName SpringCloudDemo
 * @className PaymentMain8009.java
 * @description TODO
 */
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8009 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8009.class, args);
    }
}

Ⅴ、业务类

Service

/**
 * @author PengHuAnZhi
 * @createTime 2021/2/11 16:15
 * @projectName SpringCloudDemo
 * @className PaymentService.java
 * @description TODO
 */
@Service
public class PaymentService {

    public String paymentInfo_OK(Integer id) {
        return "线程池: " + Thread.currentThread().getName()
                + "   paymentInfo_OK,id:" + id + " 正常访问!";
    }

    public String paymentInfo_Timeout(Integer id) {
        int timeNumber = 3;
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "线程池: " + Thread.currentThread().getName()
                + "   paymentInfo_OK,id:" + id + " 耗时(秒):" + timeNumber;
    }
}

Controller

/**
 * @author PengHuAnZhi
 * @createTime 2021/2/11 16:16
 * @projectName SpringCloudDemo
 * @className PaymentController.java
 * @description TODO
 */
@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @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;
    }
}

Ⅵ、测试

到这里一个简易的支付微服务搭建完毕,简单测试一下

服务降级(Hystrix断路器)

②、Jmeter开始搞事

上面这个服务,在非高并发的情形下还能维持实用,但是高并发就不一定了哦

Ⅰ、Jmeter安装

下载地址

解压缩后打开bin目录,运行jmeter.bat

服务降级(Hystrix断路器)

Ⅱ、创建测试线程组

服务降级(Hystrix断路器)

不要开太多,性能不好没准就炸了哈

服务降级(Hystrix断路器)

Ⅲ、创建请求,并配置参数

服务降级(Hystrix断路器)

服务降级(Hystrix断路器)

Ⅳ、开始压力测试

200个线程开启完毕

服务降级(Hystrix断路器)

重新访问OK请求,发现以前的秒回应到现在还要转圈圈一会后才收到回应

服务降级(Hystrix断路器)

观察控制台后发现,20000个请求打在timeout的延时方法上,所以它还在继续运行,从而把Tomcat的默认工作线程给打满了,没有多余的线程来分摊压力,难怪把本身很快的OK给拖慢了

服务降级(Hystrix断路器)

③、情况继续恶化,消费者的加入

Ⅰ、建module

服务降级(Hystrix断路器)

Ⅱ、POM

<?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>SpringCloudDemo</artifactId>
        <groupId>com.phz.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>HystrixAndFeignConsumerOrder8010</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.phz.springcloud</groupId>
            <artifactId>CloudAPI</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <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-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Ⅲ、YML

由于服务端延时3S,而Ribbon默认接收延时1S,这里需要修改,不然就会报Read timed out

server:
  port: 8010

#这里只把feign做客户端用,不注册进eureka
eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
      
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 5000
  #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的实际
  ConnectTimeout: 5000

Ⅳ、主启动

/**
 * @author PengHuAnZhi
 * @createTime 2021/2/11 17:22
 * @projectName SpringCloudDemo
 * @className OrderMain8010.java
 * @description TODO
 */
@SpringBootApplication
@EnableFeignClients
public class OrderMain8010 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain8010.class, args);
    }
}

Ⅴ、Service

/**
 * @author PengHuAnZhi
 * @createTime 2021/2/11 17:24
 * @projectName SpringCloudDemo
 * @className PaymentHystrixService.java
 * @description TODO
 */
@Component
@FeignClient(value = "hystrix-payment-service")
public interface PaymentHystrixService {

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

    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_Timeout(@PathVariable("id") Integer id);

}

Ⅵ、Controller

/**
 * @author PengHuAnZhi
 * @createTime 2021/2/11 17:27
 * @projectName SpringCloudDemo
 * @className OrderController.java
 * @description TODO
 */
@RestController
@Slf4j
public class OrderController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_Timeout(id);
        return result;
    }
}

Ⅶ、正常访问测试

都通过

服务降级(Hystrix断路器)

Ⅷ、再次使用Jmeter压测

毫无疑问会影响到OK服务的正常运行,更严重的情况可能会显示ErrorPage

服务降级(Hystrix断路器)

Ⅹ、故障原因

8009同一层次的其他接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕,8010此时调用8009,客户端访问缓慢,转圈圈。

未完待续。。。。

上一篇:Spring Cloud 之 Hystrix Turbine监控搭建(十二)


下一篇:SpringCloud - Hystrix 服务熔断,服务降级