Spring Cloud 系列之熔断器 Hystrix

1.1 简介

1.1.1 概述

  Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
   “熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

在这里插入图片描述


1.1.2 雪崩问题

  微服务中,服务间调用关系错综复杂,一个请求,可能需要调用多个微服务接口才能实现,会形成非常复杂的调用链。假如说一个业务请求需要调用 A、B、C、D 四个微服务,这四个服务有可能需要调用其他服务。此时某个服务发生异常,请求被阻塞,用户得不到响应,Tomcat 不释放线程,越来越多的服务被阻塞到这里,然而服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。
  上述例子就好比疫情期间需要大量的口罩,工厂迅速投入生产,但是熔喷布供应不足,导致口罩无法生产,陷入等待熔喷布的阶段,直到熔喷布到位才能完成生产。多条生产线都陷入了等熔喷布到位的情况,那么整个工厂就会停产,波及的范围逐渐变大。

1.1.3 解决方案

☞ 服务降级

  当服务的某个方法响应超时或者出现异常时,调用给他兜底的方法给用户返回友好信息,称之为服务降级。服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。类似于在公司中主力开发病了或者其他状况无法在进行开发了,让另外一个能力差一点的顶上去,不至于导致进度停滞。服务降级一般在客户端,但是服务端也可以使用。


☞ 服务熔断

  在一定的时间(默认 10s)内且请求量(默认 20)达到要求的情况下,服务调用的失败率达到一定的数值(默认 50%)时,该服务直接被降级处理,过一段时间后(默认 5s)尝试允许部分请求通过,请求成功则恢复链路。类似于在公司内一个员工经常出错,公司让他停职,过一段时间之后再次交给他一部分工作,完成就让他恢复原职,否则继续停职。服务熔断一般在服务端。


1.1.4 相关依赖

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

1.1.5 初识工程

☞ spring-cloud-eureka-feign 工程





1.2 服务降级

1.2.1 服务端服务降级

☞ 启动类
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 服务提供者启动类
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix	// 开启 Hystrix 支持
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

☞ fallback
/**
 * Created with IntelliJ IDEA.
 *
 * @author gaohu9712@163.com
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/provider")
public class ProviderController {

    @GetMapping("/get/{id}")
    @HystrixCommand(fallbackMethod = "getFallbackMethod")
    public Object get(Integer id) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Thread.currentThread().getName() + " --- 你已经消费了 --- " + id;
    }
    
    public Object getFallbackMethod(Integer id) {
        return Thread.currentThread().getName() + " --- 我被降级了 --- " + id;
    }

}

  由于默认是 1s 超时,所以等待 1s 后服务降级了,我们也可以在 @HystrixCommand 中修改超时时间。我们多次访问可以发现每一次的线程名称都不同,这是因为 Hystrix 使用线程将每次的请求隔离开,避免影响其他服务,就好比货船为了进行防止漏水和火灾的扩散,会将货仓分隔为多个,当发生灾害时,将所在货仓进行隔离就可以降低整艘船的风险。
在这里插入图片描述

@HystrixCommand(fallbackMethod = "getFallbackMethod", commandProperties = {
        // 响应超时时间,默认为 1s
        @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="5000")
})

1.2.2 客户端服务降级

☞ 启动类
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 消费者启动类
 */
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}

☞ fallback
/**
 * Created with IntelliJ IDEA.
 *
 * @author gaohu9712@163.com
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private ProviderFeign providerFeign;


    @GetMapping("/go/{id}")
    @HystrixCommand(fallbackMethod = "goFallbackMethod", commandProperties = {
            // 响应超时时间,默认为 1s
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1500")
    })
    public String go(@PathVariable("id") Integer id) {
        String s = providerFeign.get(id);
        return s;
    }

    public String goFallbackMethod(@PathVariable("id") Integer id) {
        return "网络状态不佳(其实是服务提供者没在规定时间响应我)";
    }
}

在这里插入图片描述
  我们可以看到根据客户端来说,服务响应超时已经被降级了,但是我们通过日志发现其实服务端还是将信息返回回来了,这就表明只要你在我规定的时间内没有返回我就不需要你了。
在这里插入图片描述


1.2.3 全局服务降级

/**
 * Created with IntelliJ IDEA.
 *
 * @author gaohu9712@163.com
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/consumer")
@DefaultProperties(defaultFallback = "goFallbackMethod")	// 在此处配上全局 fallBackMethod
public class ConsumerController {

    @Autowired
    private ProviderFeign providerFeign;


    @GetMapping("/go/{id}")
    @HystrixCommand		// 一定要加,否则不会被保护
    public String go(@PathVariable("id") Integer id) {
        String s = providerFeign.get(id);
        return s;
    }

    public String goFallbackMethod() {
        return "网络状态不佳(其实是服务提供者没在规定时间响应我)";
    }
}

在这里插入图片描述


1.2.4 Feign 服务降级

  在客户端,我们最终调用的还是 Feign 的接口来实现远程调用,我们可以直接在 Feign 中配置 fallBackMethod,和业务代码区分开让代码显得更加整洁优美。

☞ 配置文件
# 开启 Hystrix 支持
feign:
  hystrix:
    enabled: true

☞ Feign 接口
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/11/5
 * @description
 */
@Component
@FeignClient(value = "ProviderServer", fallback = FeignFallBack.class)
public interface ProviderFeign {

    @GetMapping("/provider/get/{id}")
    public String get(@PathVariable("id") Integer id);
}

☞ FeignFallBack
/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/11/6
 * @description
 */
@Component
public class FeignFallBack implements ProviderFeign {
    @Override
    public String get(Integer id) {
        return "服务端不通";
    }
}

☞ 请求结果

在这里插入图片描述




1.3 服务熔断

1.3.1 熔断模型

在这里插入图片描述
 ♞ 关闭:关闭状态(断路器关闭),所有请求都正常访问。
 ♞ 打开:打开状态(断路器打开),所有请求都会被降级。Hystrix 会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。
 ♞ 半开:半开状态,不是永久的,断路器打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会关闭断路器,否则继续保持打开,再次进行休眠计时。

1.3.2 配置熔断

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/provider")
public class ProviderController {

    @GetMapping("/get/{id}")
    @HystrixCommand(fallbackMethod = "getFallBack", 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% 后熔断
    })
    public Object get(@PathVariable("id") Integer id) {

        if (1 == id) {
           throw new  RuntimeException("id 不能为 1");
        }
        return Thread.currentThread().getName() + " --- 你已经消费了 --- " + id;
    }

    public Object getFallBack(Integer id) {
        return Thread.currentThread().getName() + " --- 服务降级, id = " + id;
    }
}

1.3.3 启动类

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 服务提供者启动类
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker	// 开启熔断
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

1.3.4 请求结果

在这里插入图片描述




1.4 其他配置

1.4.1 yml 配置

# 配置熔断策略,详细配置参考注解属性配置
hystrix:
  command:
    default:
      circuitBreaker:
        errorThresholdpPercentage: 50     # 触发熔断错误比例阈值,默认值 50%
        sleepwindowInMilliseconds: 10000  # 熔断后休眠时长,默认值 5秒
        requestvolumeThreshold: 10        # 熔断触发最小请求次数,默认值是 20
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000	  # 熔断超时设置,默认为 1秒

1.4.2 注解属性

@HystrixCommand(fallbackMethod = "fallbackMethod",
       commandProperties={
            // 设置隔离策略,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="18"),
            // 服务降级是否启用,是否执行回调函数
            @HystrixProperty(name="fallback.enabled",value="true"),
            // 是否启用断路器
            @HystrixProperty(name ="circuitBreaker.enabled",value="true"),
            // 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。默认该值为 20,
            // 如果滚动时间窗(默认 10秒)内仅收到了 19个请求,即使这 19个请求部关胶了,断路器也不会打开。
            @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="20"),
            // 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在错误率超过 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={
            // 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
            @HystrixProperty(name="coreSize",value="10"),
            // 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列
            // 否则将使用 LinkedBLockingQueue 实现的队列。
            @HystrixProperty(name="maxQueueSize",value="-1"),
            // 该参数用来为队列设置拒绝阀值。通过该参数,即使队列淀有达到最大值也能拒绝请求。
            // 该参数主要是对 LinkedBLockingQueue 队列的补充,因为 LinkedBLockingQueue
            // 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
            @HystrixProperty(name="queueSizeRejectionThreshold",value="5"),
		}
)





1.5 图形化监控 Dashboard

1.5.1 相关依赖

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

1.5.2 配置文件

server:
  port: 8888

1.5.3 启动类

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

1.5.4 启动监控


  我们发现按照要求开启监控之后,提示 Unable to connect to Command Metric Stream. 这是由于这里使用的是新版 Spring Cloud 有个巨坑官方没有处理,所以我们需要加一些东西类处理它。
在这里插入图片描述
在被监控端加上以下代码,将其交由 IoC 管理

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

若继续报 Failed opening connection to http://localhost:8091/hystrix.stream : 404 : HTTP/1.1 404 则需要在 DashBoard 服务配置文件中加入如下配置。

server:
  port: 8888

# 增加如下配置
hystrix:
  dashboard:
    proxy-stream-allow-list: "localhost"

在这里插入图片描述

在这里插入图片描述




☞ 源码



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值