天天看点

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

Hystrix断路器

  • 一、概述
  • 二、Hystrix重要概念
    • 1.服务降级
    • 2.服务熔断
    • 3.服务限流
  • 三、Hystrix案例
    • 1.服务降级
      • ①支付降级
      • ②订单降级
      • ③全局服务降级
      • ④通配服务降级FeignFallback
    • 2.服务熔断
  • 四、Hystrix工作流程
  • 五、服务监控hystrixDashboard

一、概述

分布式系统面临的问题

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

服务雪崩

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

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

所以,

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

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

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

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

官网资料: https://github.com/Netflix/Hystrix/wiki/How-To-Use

Hystrix管宣,停更进维: https://github.com/Netflix/Hystrix

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

二、Hystrix重要概念

1.服务降级

(例 :服务器忙, 请稍后再试) 遇到突发情况,异常,超时等,不让客户端等待并立刻返回一个友好提示,fallback

哪些情况会触发降级:

程序运行异常

超时

服务熔断触发服务降级

线程池/信号量打满也会导致降级

2.服务熔断

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

就是保险丝。

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

3.服务限流

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

三、Hystrix案例

构建cloud-provider-hystrix-payment-8001服务

pom文件:

<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-client</artifactId>
        </dependency>

        <!--boot web actuator-->
        <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>

        <!-- 引入自定义的API通用包 实体类 -->
        <dependency>
            <groupId>com.zzp.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>
           

application.yml配置文件:

server:
  port: 8001 # 端口号

spring:
  application:
    name: cloud-provider-hystrix-payment  # 项目名

# eureka 配置
eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
#      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
           

主启动类:

package com.zzp.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author zzp
 * @create 
 */
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {

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

           

服务类:

package com.zzp.springcloud.service;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author zzp
 * @create 
 */
@Service
public class PaymentService {

    /**
     * 正常访问
     * @param id
     * @return
     */
    public String paymentHystrixOk(Integer id){
        return "线程池: " + Thread.currentThread().getName() + " paymentHystrixOk,id: " + id
                + "\t" + "请求成功";
    }

    public String paymentHystrixTimtout(Integer id){
        Long start = System.currentTimeMillis();
        int timeCount = 3;
        try
        {
            TimeUnit.SECONDS.sleep(timeCount);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return "线程池: " + Thread.currentThread().getName() + " paymentHystrixTimtout,id: " + id
                + "\t" + "请求成功" + " 睡眠" + timeCount + "秒,请求响应耗时(毫秒): " + (System.currentTimeMillis() - start);
    }

}

           

控制类:

package com.zzp.springcloud.controller;

import com.zzp.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author zzp
 * @create 
 */
@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

    @GetMapping(value = "/payment/hystrix/ok/{id}")
    public String paymentHystrixOk(@PathVariable("id") Integer id){
        String result = paymentService.paymentHystrixOk(id);
        log.info("******响应结果result: " + result);
        return result;
    }

    @GetMapping(value = "/payment/hystrix/timeout/{id}")
    public String paymentHystrixTimtout(@PathVariable("id") Integer id){
        String result = paymentService.paymentHystrixTimtout(id);
        log.info("******响应结果result: " + result);
        return result;
    }
}

           

启动eureka7001(单服务)

启动cloud-provider-hystrix-payment-8001

访问:

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

构建cloud-consumer-feign-hystrix-order-80消费服务

pom文件:

<dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</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>
        <!-- 引入自定义的API通用包 实体类 -->
        <dependency>
            <groupId>com.zzp.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <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>
           

application.yml配置文件:

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka # 单机版

#设置feign客户端超时时间(openfeign默认支持ribbon)
ribbon:
  ReadTimeout: 5000
  ConnectTimeout: 5000
           

主启动:

package com.zzp.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author zzp
 * @create 
 */
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {

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

}

           

feign配置:

package com.zzp.springcloud.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author zzp
 * @create 
 */
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixFeign {

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

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

           

控制类:

package com.zzp.springcloud.controller;

import com.zzp.springcloud.feign.PaymentHystrixFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author zzp
 * @create 
 */
@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    private PaymentHystrixFeign paymentHystrixFeign;

    @GetMapping(value = "/consumer/payment/hystrix/ok/{id}")
    String paymentHystrixOk(@PathVariable("id") Integer id){
        String result = paymentHystrixFeign.paymentHystrixOk(id);
        return result;
    }

    @GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
    String paymentHystrixTimtout(@PathVariable("id") Integer id){
        String result = paymentHystrixFeign.paymentHystrixTimtout(id);
        return result;
    }
}

           

启动测试,访问

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

1.服务降级

①支付降级

cloud-provider-hystrix-payment-8001服务降级(fallback)

修改PaymentService类的paymentHystrixTimtout方法添加@HystrixCommand注解 @HystrixProperty(name = “execution.isolation.thread.timeoutInMilliseconds”,value = “2000”) 设置超时时间2秒

/**
     * 超时访问 运行异常 演示
     * @param id value = "2000" : 2秒
     * @return
     */
    @HystrixCommand(fallbackMethod = "paymentHystrixTimtoutHandler",
     commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")})
    public String paymentHystrixTimtout(Integer id){
        Long start = System.currentTimeMillis();
        int timeCount = 3;
        try
        {
            TimeUnit.SECONDS.sleep(timeCount);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return "线程池: " + Thread.currentThread().getName() + " paymentHystrixTimtout,id: " + id
                + "\t" + "请求成功" + " 睡眠" + timeCount + "秒,请求响应耗时(毫秒): " + (System.currentTimeMillis() - start);
    }

    public String paymentHystrixTimtoutHandler(Integer id){
        return "线程池: " + Thread.currentThread().getName() + " 系统繁忙,请求稍后重试,id: " +id + " 请求异常";
    }
           

启动类添加注解:@EnableCircuitBreaker

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {...}
           

启动测试:超时异常处理

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

修改paymentHystrixTimtout方法为运行异常 int con = 9/0;

/**
     * 超时访问 运行异常 演示
     * @param id value = "2000" : 2秒
     * @return
     */
    @HystrixCommand(fallbackMethod = "paymentHystrixTimtoutHandler",
     commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")})
    public String paymentHystrixTimtout(Integer id){
        Long start = System.currentTimeMillis();
//        int timeCount = 3;
        int con = 9/0;
//        try
//        {
//            TimeUnit.SECONDS.sleep(timeCount);
//        } catch (InterruptedException e)
//        {
//            e.printStackTrace();
//        }
        return "线程池: " + Thread.currentThread().getName() + " paymentHystrixTimtout,id: " + id
                + "\t" + "请求成功";
    }
           

重启访问:

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

上面两图故意制造异常:

  1. int con = 9/0; 计算异常
  2. 我们接受2秒钟,它运行3秒钟,超时异常

当前服务不可用,做服务降级,底层方案是paymentHystrixTimtoutHandler

②订单降级

cloud-consumer-feign-hystrix-order-80服务降级(fallback)

application.yml添加feign配置:

feign:
  hystrix:
    enabled: true
           

主启动类添加注解:@EnableHystrix

@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain80 {
           

修改控制层:修改paymentHystrixTimtout接口添加@HystrixCommand 设置超时时间1.5秒

@GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentHystrixTimtoutFallbackMethod",
            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")})
    String paymentHystrixTimtout(@PathVariable("id") Integer id){
        String result = paymentHystrixFeign.paymentHystrixTimtout(id);
        return result;
    }
    public String paymentHystrixTimtoutFallbackMethod(Integer id){
        return "消费者80,请求8001失败...";
    }
           

8001服务类设置超时为有效时间内(大于1.5秒)

启动,访问

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

修改paymentHystrixTimtout接口为运行异常

@GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentHystrixTimtoutFallbackMethod",
            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")})
    String paymentHystrixTimtout(@PathVariable("id") Integer id){
        int l = 8/0;
        String result = paymentHystrixFeign.paymentHystrixTimtout(id);
        return result;
    }
    public String paymentHystrixTimtoutFallbackMethod(Integer id){
        return "消费者80运行异常,请求8001失败...";
    }
           

重启,访问

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

③全局服务降级

OrderHystrixController类添加注解:@DefaultProperties

@DefaultProperties(defaultFallback = “”)

1: 1 每个方法配置一个服务降级方法,技术上可以,实际上代码冗余

2:N除了个别核心业务有专属,其他普通的可以通过@DefaultProperties(defaultFallback = “”)统一跳转到统一处理结果页面

通用和独享的各自分开,避免代码膨胀,合理减少代码量

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "paymentGlobalFallbackMethod")
public class OrderHystrixController {...}
           

OrderHystrixController类添加paymentGlobalFallbackMethod全局处理方法

// 下面是全局fallback方法
    public String paymentGlobalFallbackMethod(){
        return "GlobalFallback异常处理,等待....";
    }
           

OrderHystrixController 下修改paymentHystrixTimtout接口不指定的异常处理方法

@GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
//    @HystrixCommand(fallbackMethod = "paymentHystrixTimtoutFallbackMethod",
//            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")})
    @HystrixCommand
    String paymentHystrixTimtout(@PathVariable("id") Integer id){
        int l = 8/0;
        String result = paymentHystrixFeign.paymentHystrixTimtout(id);
        return result;
    }
           

测试:

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

④通配服务降级FeignFallback

在80客户端添加PaymentHystrixFeignFallback 类实现PaymentHystrixFeign类

package com.zzp.springcloud.feign;

import org.springframework.stereotype.Component;

/**
 * @author zzp
 * @create
 */
@Component
public class PaymentHystrixFeignFallback implements PaymentHystrixFeign {

    @Override
    public String paymentHystrixOk(Integer id) {
        return " fallback >>>>  服务降级 paymentHystrixOk接口";
    }

    @Override
    public String paymentHystrixTimtout(Integer id) {
        return "fallback >>>>  服务降级 paymentHystrixTimtout接口";
    }
}

           

PaymentHystrixFeign 添加fallback参数指定实现类

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentHystrixFeignFallback.class)
public interface PaymentHystrixFeign {...}

           

启动7001,8001,80服务

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

把8001服务停掉,再访问

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

2.服务熔断

熔断机制概述

熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长,会进行服务降级,进而熔断该节点服务的调用,快速放回错误的响应息。

当检测到该节点微服务调用响应正常后,恢复调用链路。

在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用状况,当失败的调用到一定阀值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。

大神论文: https://martinfowler.com/bliki/CircuitBreaker.html

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

修改cloud-provider-hystrix-payment-8001服务PaymentService类添加方法:

@HystrixCommand(fallbackMethod = "paymentCircuitbreakerFallback",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"), // 失败率达到多少后跳闸
    })
    public String paymentCircuitbreaker(@PathVariable("id") Integer id){
        if(id < 0){
            throw new RuntimeException("*******id  不能为负数");
        }

        // IdUtil.simpleUUID() = UUID.randomUUID().toString().replace("-","")
        String simpleUUID = IdUtil.simpleUUID();
        return "线程名称: " + Thread.currentThread().getName() + "\t" + "调用成功,流水号:" +simpleUUID;
    }
    public String paymentCircuitbreakerFallback(@PathVariable("id") Integer id){
        return "id 不能为负数,请重试,id: " +id;
    }
           

Command Properties官网配置: https://github.com/Netflix/Hystrix/wiki/Configuration#ThreadPool

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

@HystrixProperty配置参数 参考源码com.netflix.hystrix.HystrixCommandProperties类默认值

下面是其中一段

protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder, String propertyPrefix) {
        this.key = key;
        this.circuitBreakerEnabled = getProperty(propertyPrefix, key, "circuitBreaker.enabled", builder.getCircuitBreakerEnabled(), default_circuitBreakerEnabled);
        this.circuitBreakerRequestVolumeThreshold = getProperty(propertyPrefix, key, "circuitBreaker.requestVolumeThreshold", builder.getCircuitBreakerRequestVolumeThreshold(), default_circuitBreakerRequestVolumeThreshold);
        this.circuitBreakerSleepWindowInMilliseconds = getProperty(propertyPrefix, key, "circuitBreaker.sleepWindowInMilliseconds", builder.getCircuitBreakerSleepWindowInMilliseconds(), default_circuitBreakerSleepWindowInMilliseconds);
        this.circuitBreakerErrorThresholdPercentage = getProperty(propertyPrefix, key, "circuitBreaker.errorThresholdPercentage", builder.getCircuitBreakerErrorThresholdPercentage(), default_circuitBreakerErrorThresholdPercentage);
        this.circuitBreakerForceOpen = getProperty(propertyPrefix, key, "circuitBreaker.forceOpen", builder.getCircuitBreakerForceOpen(), default_circuitBreakerForceOpen);
        this.circuitBreakerForceClosed = getProperty(propertyPrefix, key, "circuitBreaker.forceClosed", builder.getCircuitBreakerForceClosed(), default_circuitBreakerForceClosed);
        this.executionIsolationStrategy = getProperty(propertyPrefix, key, "execution.isolation.strategy", builder.getExecutionIsolationStrategy(), default_executionIsolationStrategy);
        this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, key, "execution.isolation.thread.timeoutInMilliseconds", builder.getExecutionIsolationThreadTimeoutInMilliseconds(), default_executionTimeoutInMilliseconds);
        this.executionTimeoutEnabled = getProperty(propertyPrefix, key, "execution.timeout.enabled", builder.getExecutionTimeoutEnabled(), default_executionTimeoutEnabled);
        this.executionIsolationThreadInterruptOnTimeout = getProperty(propertyPrefix, key, "execution.isolation.thread.interruptOnTimeout", builder.getExecutionIsolationThreadInterruptOnTimeout(), default_executionIsolationThreadInterruptOnTimeout);
        this.executionIsolationThreadInterruptOnFutureCancel = getProperty(propertyPrefix, key, "execution.isolation.thread.interruptOnFutureCancel", builder.getExecutionIsolationThreadInterruptOnFutureCancel(), default_executionIsolationThreadInterruptOnFutureCancel);
        this.executionIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, key, "execution.isolation.semaphore.maxConcurrentRequests", builder.getExecutionIsolationSemaphoreMaxConcurrentRequests(), default_executionIsolationSemaphoreMaxConcurrentRequests);
        this.fallbackIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, key, "fallback.isolation.semaphore.maxConcurrentRequests", builder.getFallbackIsolationSemaphoreMaxConcurrentRequests(), default_fallbackIsolationSemaphoreMaxConcurrentRequests);
        this.fallbackEnabled = getProperty(propertyPrefix, key, "fallback.enabled", builder.getFallbackEnabled(), default_fallbackEnabled);
        this.metricsRollingStatisticalWindowInMilliseconds = getProperty(propertyPrefix, key, "metrics.rollingStats.timeInMilliseconds", builder.getMetricsRollingStatisticalWindowInMilliseconds(), default_metricsRollingStatisticalWindow);
        this.metricsRollingStatisticalWindowBuckets = getProperty(propertyPrefix, key, "metrics.rollingStats.numBuckets", builder.getMetricsRollingStatisticalWindowBuckets(), default_metricsRollingStatisticalWindowBuckets);
        this.metricsRollingPercentileEnabled = getProperty(propertyPrefix, key, "metrics.rollingPercentile.enabled", builder.getMetricsRollingPercentileEnabled(), default_metricsRollingPercentileEnabled);
        this.metricsRollingPercentileWindowInMilliseconds = getProperty(propertyPrefix, key, "metrics.rollingPercentile.timeInMilliseconds", builder.getMetricsRollingPercentileWindowInMilliseconds(), default_metricsRollingPercentileWindow);
        this.metricsRollingPercentileWindowBuckets = getProperty(propertyPrefix, key, "metrics.rollingPercentile.numBuckets", builder.getMetricsRollingPercentileWindowBuckets(), default_metricsRollingPercentileWindowBuckets);
        this.metricsRollingPercentileBucketSize = getProperty(propertyPrefix, key, "metrics.rollingPercentile.bucketSize", builder.getMetricsRollingPercentileBucketSize(), default_metricsRollingPercentileBucketSize);
        this.metricsHealthSnapshotIntervalInMilliseconds = getProperty(propertyPrefix, key, "metrics.healthSnapshot.intervalInMilliseconds", builder.getMetricsHealthSnapshotIntervalInMilliseconds(), default_metricsHealthSnapshotIntervalInMilliseconds);
        this.requestCacheEnabled = getProperty(propertyPrefix, key, "requestCache.enabled", builder.getRequestCacheEnabled(), default_requestCacheEnabled);
        this.requestLogEnabled = getProperty(propertyPrefix, key, "requestLog.enabled", builder.getRequestLogEnabled(), default_requestLogEnabled);
        this.executionIsolationThreadPoolKeyOverride = HystrixPropertiesChainedProperty.forString().add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", (Object)null).build();
    }
           

PaymentController类添加接口

@GetMapping(value = "/payment/circuit/{id}")
    public String paymentCircuitbreaker(@PathVariable("id") Integer id){
        return paymentService.paymentCircuitbreaker(id);
    }
           

启动:访问

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

快速访问多次,再请求:

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

说明超出请求次数熔断了,然后慢慢恢复

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

小总结:

熔断类型:

  • 熔断打开

    请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态

  • 熔断关闭

    熔断关闭不会对服务进行熔断

  • 熔断半开

    部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断

官网断路器流程图:

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

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

  1. 快照时间窗:

    断路器确定是否需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒

  2. 请求总数阀值:

    在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果改hystrix命令的调用次数不足20次,即使所有的请求超时或其他原因失败,断路器都不会打开。

  3. 错误百分比阀值

    当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开。

断路器开启或者关闭的条件:

  1. 当满足一定的阀值的时候(默认10秒超过20个请求次数)
  2. 当失败率达到一定的时候(默认10秒超过50%的请求失败)
  3. 达到以上阀值,断路器将会开启
  4. 当开启的时候,所有请求都不会进行转发
  5. 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。

断路器打开后:

  1. 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并降级逻辑切换为主逻辑,减少响应延迟的效果。
  2. 原来的主逻辑要如何恢复呢?

    对于这一问题,hystrix也为我们实现了自动恢复功能。

    当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,

    当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,

    主逻辑恢复,如果这次请求依然问题,断路器继续进入半打开状态,休眠时间窗重新计时。

整合配置:

@HystrixCommand(fallbackMethod = "str_flallbackMethon", // 定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
            groupKey = "strGroupCommand", // HystrixCommand 命令所属的组的名称:默认注解方法类的名称
            commandKey = "strCommand", // HystrixCommand 命令的key值,默认值为注解方法的名称
            threadPoolKey = "strThreadPool",  // 线程池名称,默认定义为groupKey

            commandProperties = { // 配置hystrix命令的参数
                        // 设置隔离策略 THREAD:表示线程池 SEMAPHORE:信号池隔离
                    @HystrixProperty(name = "execution.isolation.strategy",value = "THREAD"),
                        // 但隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "10"),
                        // 配置命令执行的超时时间
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "10"),
                        // 是否启用超时时间
                    @HystrixProperty(name = "execution.timeout.enabled",value = "true"),
                        // 执行超时的时候是否中断
                    @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout",value = "true"),
                        // 执行被取消的时候是否中断
                    @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel",value = "true"),
                        // 允许回调方法执行的最大并发数
                    @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests",value = "10"),
                        // 服务降级是否启用,是否执行回调函数
                    @HystrixProperty(name = "fallback.enabled",value = "true"),
                        // 是否启用断路器
                    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
                        // 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候
                    // 如果滚动时间窗(默认10秒)内仅收到19个请求,即使这19个请求都失败,断路器都不会打开。
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"),
                        // 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过
                    // circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50,
                        // 就把断路器设置为 “打开” 状态,否则就设置为 “关闭” 状态
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),
                        // 该属性用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,
                    // 会将断路器设置为 “半开” 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 “打开” 状态,
                    // 如果成功就设置为 “关闭” 状态。
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
                        // 断路器强制打开
                    @HystrixProperty(name = "circuitBreaker.forceOpen",value = "false"),
                        // 断路器强制关闭
                    @HystrixProperty(name = "circuitBreaker.forceClosed",value = "false"),
                        // 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "10000"),
                        // 该属性用来设置滚动时间窗统计指标信息划分 “桶” 的数量,断路器在收集指标信息的时候会根据
                    // 设置的时间窗长度拆分成多个 “桶” 来累计各度量值,每个 “桶” 记录了一段时间内的采集指标。
                    // 比如 10 秒内拆分成 10 个“桶”收集这样,所以 timeInMilliseconds 必须能被 numBuckets 整除。否则会抛出异常
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets",value = "10"),
                        // 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false,那么所有的概念要统计都将返回 -1
                    @HystrixProperty(name = "metrics.rollingPercentile.enabled",value = "false"),
                        // 该属性用来设置百分位数统计滚动窗口的持续时间,单位为毫秒
                    @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds",value = "60000"),
                        // 该属性用来设置百分位数统计滚动窗口中使用 “桶” 的数量。
                    @HystrixProperty(name = "metrics.rollingPercentile.numBuckets",value = "60000"),
                        // 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设置的执行次数,
                    // 就会从最初的位置开始重写。例如,将该值设置为100,滚动窗口为10秒,若在10秒内一个 “桶” 中发生了500次执行,
                    // 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
                    @HystrixProperty(name = "metrics.rollingPercentile.bucketSize",value = "100"),
                        // 该属性用来设置采集影响断路器状态的健康快照(请求的成功、错误百分比)的间隔等待时间。
                    @HystrixProperty(name = "metrics.healthSnapshot.intervalInMilliseconds",value = "500"),
                        // 是否开启请求缓存
                    @HystrixProperty(name = "requestCache.enabled",value = "true"),
                        // HystrixCommand的执行和事件是否打印日记到 HystrixRequestLog 中
                    @HystrixProperty(name = "requestLog.enabled",value = "true"),
            },
            threadPoolProperties = { // 配置hystrix依赖的线程池的参数
                        // 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
                    @HystrixProperty(name = "coreSize",value = "10"),
                        // 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,
                    // 否则将使用 linkedBlockingQueue 实现的队列.
                    @HystrixProperty(name = "maxQueueSize",value = "-1"),
                        // 该属性用来为队列设置拒绝阀值。通过该参数,即使队列没有达到最大值也能拒绝请求。
                    // 该参数只有对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue
                        // 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了
                    @HystrixProperty(name = "queueSizeRejectionThreshold",value = "5")
            }
    )
    public String strConsumer(@PathVariable("id") Integer id){...}
           

四、Hystrix工作流程

官网: https://github.com/Netflix/Hystrix/wiki/How-it-Works

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

流程图

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

五、服务监控hystrixDashboard

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

新建 仪表盘9001

新建cloud-consumer-hystrix-dashboard-9001服务作为Hystrix Dashboard服务

Hystrix Dashboard主要依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
           

pom文件:

<dependencies>
        <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>
           

application.yml文件

server:
  port: 9001
           

主启动类添加:@EnableHystrixDashboard

/**
 * @author zzp
 * @create
 */
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class);
    }
}
           
  • 所有的provider服务提供方微服务(如我们的8001/8002/8003)都需要监控依赖配置:
<!--actuator监控信息完善-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
           

启动9001

浏览器访问: http://localhost:9001/hystrix

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

为了让服务提供方的服务能被Hystrix Dashboard监控到,需要在提供方cloud-provider-hystrix-payment-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;
    }

}
           

provider服务的pom文件的依赖必须有:

<!--boot web actuator-->
        <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>
           

启动7001和8001

9001监控8001

在Hystrix Dashboard的图形化界面中输入要监控的服务提供者:http://localhost:8001/hystrix.stream

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

如何看图

7色

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

1圈

实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度色从 绿色<黄色<橙色<红色 递减。

该实心圆除了颜色变化之外,它的大小也会根据实例请求流量发送变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以再大量的实例中快速的发现故障实例和高压力实例

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

1线

曲线:用来记录2分钟内流量的相对比,可以通过它来观察到流量的上升和下降趋势

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard

整图说明:

springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard
springcloud(八)--Hystrix断路器一、概述二、Hystrix重要概念三、Hystrix案例四、Hystrix工作流程五、服务监控hystrixDashboard