SpringCloud学习笔记(五)----服务降级Hystrix

Hystrix断路器

概述

分布式面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
SpringCloud学习笔记(五)----服务降级Hystrix_第1张图片
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的"扇出"。

如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应"。

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

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

Hystrix是什么

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败。比如超时、异常等。

Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

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

Hystrix能干嘛

  1. 对通过第三方客户端库访问的依赖项(通常是通过网络)的延迟和故障进行保护和控制。
  2. 在复杂的分布式系统中阻止级联故障。
  3. 快速失败,快速恢复。
  4. 回退,尽可能优雅地降级。
  5. 启用近实时监控、警报和操作控制。

Hystrix重要概念

服务降级

比如服务器忙,不让客户端等待并立刻返回一个友好提示,fallback。

下列情况都会触发降级

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

服务熔断

当某个服务出现问题,卡死了,不能让用户一直等待,需要关闭所有对此服务的访问,然后调用服务降级。

三步骤: 服务的降级->进而熔断->恢复调用链路。

服务限流

秒杀高并发等操作,不能让所有访问用户瞬间都访问服务器,限制一次只可以有多少请求。

Hystrix案例

构建

1.新建cloud-provider-hystrix-payment8001
2.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>cloud123</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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>


        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</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>

2.application.yml

server:
  port: 8001


eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      # defaultZone: http://eureka7002.com:7002/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/
#  server:
#    enable-self-preservation: false
spring:
  application:
    name: cloud-provider-hystrix-payment
#    eviction-interval-timer-in-ms: 2000

3.主启动


@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
     

    public static void main(String[] args) {
     
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

4.业务类
4.1service(PaymentService)

@Service
public class PaymentService {
     

    //成功
    public  String paymentInfo_OK(Integer id){
     
        return  "线程池:"+Thread.currentThread().getName()+ "   paymentInfo_OK,id:"+id+"\t"+"哈哈哈";
    }

    //失败
    public  String paymentInfo_TimeOut(Integer id){
     
        //延时3s执行,模拟处理业务复杂
        int timeNumber = 3;
        try{
     
            TimeUnit.SECONDS.sleep(timeNumber);
        }catch (Exception e){
     
            e.printStackTrace();
        }
        return  "线程池:"+Thread.currentThread().getName()+ "   paymentInfo_TimeOut,id:"+id+"\t"+"哈哈哈"+" 耗时(秒):"+timeNumber;
    }
}

4.2controller(PaymentController)

@RestController
@Slf4j
public class PaymentController {
     

    @Autowired
    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压测工具,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务,压测中,发现,另外一个方法并没有被压测,但是我们访问它时,却需要等待,这就是因为被压测的方法它占用了服务器大部分资源,导致其他请求也变慢了。

一句话来说:tomcat的默认工作线程数被打满了,没有多余的线程来分解压力和处理。

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

新建消费者Module

1.新建cloud-consumer-feign-hystrix-order80 Module。

2.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>cloud123</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>
        <!--新增hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</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>

3.application.yml

eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
             defaultZone: http://eureka7001.com:7001/eureka/

spring:
  application:
    name: cloud-provider-hystrix-order
server:
  port: 80

4.主启动类

@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
     
    public static void main(String[] args) {
     
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

5.业务类
5.1 PaymentHystrixService

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
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);
}

5.2 OrderHystrixController

@RestController
@Slf4j
public class OrderHystrixController {
     

    @Autowired
    private PaymentHystrixService paymentHystrixServicel;

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


    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
     
        return  paymentHystrixServicel.paymentInfo_TimeOut(id);
    }
}

启动order80模块,访问pay,再次压测20000个并发,此时80消费者要么转圈圈等待,访问变慢,要么消费者报超时错误。

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

现在我们有两个问题需要解决:

  • 超时导致服务器变慢(转圈)
  • 出错(宕机或程序运行出错)

解决办法

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

服务降级

8001服务降级

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

在业务类启动@HystrixCommand注解

@Service
public class PaymentService {
     

    //成功
    public  String paymentInfo_OK(Integer id){
     
        return  "线程池:"+Thread.currentThread().getName()+ "   paymentInfo_OK,id:"+id+"\t"+"哈哈哈";
    }

    //失败
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
     
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")  //3秒钟以内就是正常的业务逻辑

    })
    public  String paymentInfo_TimeOut(Integer id){
     
        //延时3s执行,模拟处理业务复杂
        int timeNumber = 5;
        try{
     
            TimeUnit.SECONDS.sleep(timeNumber);
        }catch (Exception e){
     
            e.printStackTrace();
        }
        return  "线程池:"+Thread.currentThread().getName()+ "   paymentInfo_TimeOut,id:"+id+"\t"+"哈哈哈"+" 耗时(秒):"+timeNumber;
    }

    //兜底方法
    public String paymentInfo_TimeOutHandler(Integer id ){
     
        return  "线程池:"+Thread.currentThread().getName()+ "   系统繁忙,请稍候再试,id:"+id+"\t"+"哭了吧唧";
    }
}

一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法。
比如上述方法中,我们设置了@HystrixProperty(name = “execution.isolation.thread.timeoutInMilliseconds”,value = “3000”),也就是说,3s之内执行完这个业务方法是正确的,一旦这个业务方法执行超过3s,就会自动会调用paymentInfo_TimeOutHandler方法。

在主启动类中进行激活,添加@EnableCircuitBreaker

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
     

    public static void main(String[] args) {
     
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

上面只是超时测试,我们也可以对该方法进行错误测试,修改代码如下:

  //失败
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
     
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")  //3秒钟以内就是正常的业务逻辑

    })
    public  String paymentInfo_TimeOut(Integer id){
     
        //延时3s执行,模拟处理业务复杂
//        int timeNumber = 5;
//        try{
     
//            TimeUnit.SECONDS.sleep(timeNumber);
//        }catch (Exception e){
     
//            e.printStackTrace();
//        }
        int i = 10/0; //故意出错。
        return  "线程池:"+Thread.currentThread().getName()+ "   paymentInfo_TimeOut,id:"+id+"\t"+"哈哈哈"+" 耗时(秒):";
    }

只要当前服务不可用了(超时或异常),就做服务降级,兜底的方案都是paymentInfo_TimeOutHandler。

测试结果如下:
在这里插入图片描述

80服务降级

80订单微服务,也可以更好的保护自己。可以根据8001服务依然画葫芦进行服务降级。

1.修改YML

eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
             defaultZone: http://eureka7001.com:7001/eureka/

spring:
  application:
    name: cloud-provider-hystrix-order
server:
  port: 80

feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

2.主启动类上添加@EnableHystrix注解

@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain80 {
     
    public static void main(String[] args) {
     
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

3.修改contrller

@RestController
@Slf4j
public class OrderHystrixController {
     

    @Autowired
    private PaymentHystrixService paymentHystrixServicel;

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


    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
     
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")  //1.5秒钟以内就是正常的业务逻辑
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
     
        String result = paymentHystrixServicel.paymentInfo_TimeOut(id);
        return result;
    }

    //兜底方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
     
        return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
    }

}

测试结果如下:
在这里插入图片描述

目前问题

  1. 降级方法与业务方法写在了一块,耦合度高。
  2. 每个业务方法都写了一个降级方法,重复代码多。

解决问题

解决重复代码的问题

配置一个全局的降级方法,所有方法都可以走这个降级方法,至于某些特殊创建,再单独创建降级方法。

@DefaultProperties(defaultFallback="")注解,为所有方法指定统一的降级方法,前提是当前方法需要指定@HystrixCommand注解,如果@HystrixCommand注解指定了降级方法,就用@HystrixCommand注解指定的,如果没有就用全局降级方法。

设计理念

  • 1:1每个方法配置一个服务降级方法,技术上可以,实际上傻X。
  • 1:N除了个别重要核心业务有专属,其它普通的可以通过@DefaultProperties(defaultFallback="")统一跳转到统一处理结果页面。
  • 通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量。

1.修改80模块的controller代码如下:

@RestController
@Slf4j
//指定这个方法为全局的降级方法,当业务类没有指定降级方法时,
//就使用这个方法作为降级方法,前提业务量有@HystrixCommand注解。
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
     

    @Autowired
    private PaymentHystrixService paymentHystrixServicel;

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


    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
//    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
     
//            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")  //3秒钟以内就是正常的业务逻辑
//    })
    @HystrixCommand //不指定降级方法,就使用全局设置的降级方法
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
     
        int age = 1/0;
        String result = paymentHystrixServicel.paymentInfo_TimeOut(id);
        return result;
    }

    //兜底方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
     
        return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
    }

    //下面是全局fallback方法
    public String payment_Global_FallbackMethod(){
     
        return "Global异常处理信息,请稍后再试,(┬_┬)";
    }

}

测试:
在这里插入图片描述

解决代码耦合度的问题

需求:客户端去调用服务端,碰上服务端宕机或关闭。
本次案例服务降级处理是再客户端80实现完成的,服务端8001就不服务降级了。我们只需要再Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。

1.根据80模块已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理。

@Component
public class PaymentFallbackService implements  PaymentHystrixService {
     
    @Override
    public String paymentInfo_OK(Integer id) {
     
        return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";

    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
     
        return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";

    }
}

PayService接口是远程调用8001模块,当调用8001模块中的方法时,出现了错误时,就需要降级方法来进行处理。
实现类中的方法,刚好与接口中的方法一一对应。并且实现类中的方法作为降级方法,当客户端调用接口中的方法出错时,就会调用实现类中对应的降级方法进行处理。

2.修改配置文件,添加:

feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

3.让PayService的实现类生效:

在@FeignClient注解中指定fallback属性值为PayService接口的实现类字节码文件,代码如下:

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
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);
}
运行逻辑

当请求过来,首先还是通过Feign远程调用8001模块对应的方法,但是如果8001模块出错,调用失败,那么就会调用PaymentFallbackService类的当前同名的方法,作为降级方法。

测试:
此时将8001模块服务关闭,来演示服务宕机的错误,80模块再次访问:
在这里插入图片描述
可以看到,并没有报800错误,而是降级访问实现类的同名方法,这样即使服务器挂了,用户不用一直等待,或者报错。

问题:这样虽然解决了代码耦合度文图,但是又出现了过多重复代码的问题,每个方法都有一个降级方法。

服务熔断

概述

类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。
就是保险丝:服务的降级->进而熔断->恢复调用链路。

比如并发到达10000后,我们就拒绝其他用户访问,如果在有用户访问,就调用降级方法返回给用户。

熔断机制概述:
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在SpringCloud框架里,熔断机制通过Hystrix实现。Hystrix会监控服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。

代码实现

修改cloud-provider-hystrix-payment8001

1.修改PayService接口,添加服务熔断相关的方法

 //服务熔断                                     //降级方法
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
     
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //请求次数   并发超过这个次数,开启断路器
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //时间范围
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸  60%
                                                                              //比如,10次请求,6次失败,则跳闸。
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
     
        if (id < 0){
     
            throw new RuntimeException("*****id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
     
        return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
    }

@HystrixProperty注解整体意思是:
10秒之内(窗口,会移动),如果并发超过10个,或者10个并发中,失败了6个,就开启熔断器。

涉及到断路器的三个重要参数:快照时间窗,请求总数阈值、错误百分比阈值。

  • 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
  • 请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断,默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
  • 错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。

IdUtil是Hutool包下的类,这个Hutool就是整合了所有的常用方法,比如UUID,反射,IO流等工具方法什么的都整合了。

SpringCloud学习笔记(五)----服务降级Hystrix_第2张图片
断路器的打开和关闭,是按照以下5步决定的:

  1. 并发此时是否达到我们指定的阈值。
  2. 错误百分比,比如我们配置了60%,那么如果并发请求中,10次有6次是失败的,就开启断路器。
  3. 上面的条件符合,断路器改变状态为open(开启)。
  4. 这个服务的断路器开启,所有请求无法访问。
  5. 在我们的时间窗口期间,尝试让一些请求通过(半开状态),如果请求还是失败,证明断路器还是开启状态,服务没有恢复。如果请求成功了,证明服务已经恢复,断路器状态变为close关闭状态。

@HystrixProperty注解name属性可配置哪些值,可以去 HystrixCommandProperties抽象类去了解。

2.修改controller
添加一个测试方法

 //===服务熔断
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
     
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("*******result:"+result);
        return result;
    }

测试

启动80、8001模块
多次访问,并且错误率超过60%。
在这里插入图片描述
此时服务熔断,此时即使访问正确的也会报错。

在这里插入图片描述
但是,当过了几秒后,又恢复了。
因为在10 秒窗口期内,它自己会尝试接收部分请求,发现服务可以正常调用,慢慢的当错误率低于60%,取消熔断器。

总结

熔断类型
  • 熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入熔断状态。
  • 熔断关闭:熔断关闭不会对服务进行熔断
  • 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。
熔断器打开之后

SpringCloud学习笔记(五)----服务降级Hystrix_第3张图片

All配置

SpringCloud学习笔记(五)----服务降级Hystrix_第4张图片
SpringCloud学习笔记(五)----服务降级Hystrix_第5张图片
SpringCloud学习笔记(五)----服务降级Hystrix_第6张图片
SpringCloud学习笔记(五)----服务降级Hystrix_第7张图片

hystrix工作流程

SpringCloud学习笔记(五)----服务降级Hystrix_第8张图片

1请求进来,首先查询缓存,如果缓存有,直接返回
  	如果缓存没有,--->2
2,查看断路器是否开启,如果开启的,Hystrix直接将请求转发到降级返回,然后返回
  	如果断路器是关闭的,
				判断线程池等资源是否已经满了,如果已经满了
  					也会走降级方法
  			如果资源没有满,判断我们使用的什么类型的Hystrix,决定调用构造方法还是run方法
        然后处理请求
        然后Hystrix将本次请求的结果信息汇报给断路器,因为断路器此时可能是开启的
          			(因为断路器开启也是可以接收请求的)
        		断路器收到信息,判断是否符合开启或关闭断路器的条件,
				如果本次请求处理失败,又会进入降级方法
        如果处理成功,判断处理是否超时,如果超时了,也进入降级方法
        最后,没有超时,则本次请求处理成功,将结果返回给controller

服务监控hystrixDashboard

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控可视化界面。

仪表盘9001

1.新建cloud-consumer-hystrix-dashboard9001
2.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>cloud123</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>
    <dependencies>
        <!--新增hystrix dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>

3.application.yml

server:
  port: 9001

4.主启动类
需要添加新注解@EnableHystrixDashboard

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
     
    public static void main(String[] args) {
     
        SpringApplication.run(HystrixDashboardMain9001.class,args);
    }
}

5.所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

之前的pom文件中都已经添加过了,这个是springboot的监控组件。

6.启动9001即可,访问:localhost:9001/hystrix,出现如下界面:
SpringCloud学习笔记(五)----服务降级Hystrix_第9张图片

7.注意,此时仅仅是可以访问HystrixDashboard,并不代表已经监控了8001,8002,如果要监控,还需要配置。

以8001为例,在主启动类添加

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
     

    public static void main(String[] args) {
     
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }


    /**
     *  此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     *  ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
     *  只要再自己的项目里配置上下面的servlet就可以了
     * */
    @Bean
    public ServletRegistrationBean getServlet(){
     
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }

}

其余的8002,8003等也是如此。

8.启动7001,8001,9001
然后在web界面,指定9001要监控8001:
SpringCloud学习笔记(五)----服务降级Hystrix_第10张图片
SpringCloud学习笔记(五)----服务降级Hystrix_第11张图片
实心圈的含义:
它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。

曲线的含义:
用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
SpringCloud学习笔记(五)----服务降级Hystrix_第12张图片
SpringCloud学习笔记(五)----服务降级Hystrix_第13张图片

你可能感兴趣的:(SpringCloud)