SpringCloud(六)熔断器之Hystrix

本文详细介绍了微服务中的雪崩效应及其原因,阐述了服务熔断、服务降级和限流等解决方案。重点讲解了Netflix的Hystrix库在服务容错中的应用,包括Hystrix的线程池隔离策略、熔断工作流程和配置选项,以及如何通过Hystrix实现服务的快速失败和降级处理,以提高系统的稳定性和容错性。
摘要由CSDN通过智能技术生成

SpringCloud(六)熔断器之Hystrix

属于一种容错机制

微服务中的雪崩效应

当山坡积雪内部的内聚力抗拒不了它所受到的重力拉引时,便向下滑动,引起大量雪体崩塌,人们把这种自然现象称作雪崩。

微服务中,一个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路。

服务雪崩效应:是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

扇入:代表着该微服务被调用的次数,扇入大,说明该模块复用性好

扇出:该微服务调用其他微服务的个数,扇出大,说明业务逻辑复杂

扇入大是一个好事,扇出大不一定是好事

在微服务架构中,一个应用可能会有多个微服务组成,微服务之间的数据交互通过远程过程调用完成。这就带来一个问题,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。

如图中所示,最下游商品微服务响应时间过长,大量请求阻塞,大量线程不会释放,会导致服务器资源耗尽,最终导致上游服务甚至整个系统瘫痪。

形成原因

服务雪崩的过程可以分为三个阶段:

  1. 服务提供者不可用
  2. 重试加大请求流量
  3. 服务调用者不可用

服务雪崩的每个阶段都可能由不同的原因造成:

在这里插入图片描述

雪崩效应解决方案

从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;

三种技术手段应对微服务中的雪崩效应,这三种手段都是从系统可用性、可靠性角度出发,尽量防止系统整体缓慢甚至瘫痪。

服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制。我们在各种场景下都会接触到熔断这两个字。高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。股票交易中,如果股票指数过高,也会采用熔断机制,暂停股票的交易。同样,在微服务架构中,熔断机制也是起着类似的作用。当扇出链路的某个微服务不可用或者响应时间太长时,熔断该节点微服务的调用,进行服务的降级,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。

注意

1)服务熔断重点在“”,切断对下游服务的调用

2)服务熔断和服务降级往往是一起使用的,Hystrix就是这样。

服务降级

通俗讲就是整体资源不够用了,先将一些不关紧的服务停掉(调用我的时候,给你返回一个预留的值,也叫做兜底数据),待渡过难关高峰过去,再把那些服务打开。

服务降级一般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调用,此刻客户端可以自己准备一个本地的fallback回调,返回一个缺省值,这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。

服务限流

服务降级是当服务出问题或者影响到核心流程的性能时,暂时将服务屏蔽掉,待高峰或者问题解决后再打开;但是有些场景并不能用服务降级来解决,比如秒杀业务这样的核心功能,这个时候可以结合服务限流来限制这些场景的并发/请求量

限流措施也很多,比如

  • 限制总并发数(比如数据库连接池、线程池)
  • 限制瞬时并发数(如nginx限制瞬时并发连接数)
  • 限制时间窗口内的平均速率(如Guava的RateLimiter【令牌桶算法】、nginx的limit_req模块【漏桶算法】,限制每秒的平均速率)【可以理解为窗格(A -> B)所用的时间即为窗格】
  • 限制远程接口调用速率、限制MQ的消费速率等

Hystrix简介

[**来自官网]**Hystrix(豪猪),宣言“defend your application”是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

  • 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑。 页面静态化微服务方法(@HystrixCommand 添加Hystrix控制)
  • 跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以跳闸,停止请求该服务一段时间。
  • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(舱壁模式)。如果该线程池已满, 发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
  • 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。
  • 自我修复:断路器打开一段时间后,会自动进入“半开”状态(探测服务是否可用,如还是不可用,再次退回打开状态)。

Hystrix应用

熔断处理

目的:商品微服务长时间没有响应,服务消费者—>页面静态化微服务快速失败给用户提示

  • 引入依赖:服务消费者工程(静态化微服务)中引入Hystrix依赖坐标(也可以添加在父工程中)

    <!--熔断器Hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    
  • 开启熔断:服务消费者工程(静态化微服务)的启动类中添加熔断器开启注解【@EnableCircuitBreaker】

    @SpringBootApplication
    //@EnableEurekaClient         //Eureka客户端,只能在Eureka环境中使用
    @EnableDiscoveryClient      //也是将当前项目表示为注册中心的客户端,像注册中心进行注册,可以在所有的服务注册中心环境中使用
    @EnableCircuitBreaker       //开启服务熔断机制
    public class PageApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(PageApplication.class,args);
        }
    
        /**
         * 向容器中注入一个RestTemplate,封装了HttpClient
         * @return
         */
        @Bean
        @LoadBalanced       //Ribbon负载均衡
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
    }
    
  • 定义服务降级处理方法:业务方法上使用@HystrixCommand的fallbackMethod属性关联到服务降级处理方法

    /**
     * 模拟服务超时,熔断处理
     * 针对熔断处理,Hystrix默认维护一个线程池,默认大小为10,线程队列默认不启用
     * @return
     */
    @HystrixCommand(
            threadPoolKey = "getProductServerPort2",         //threadPoolKey给自己维护的线程池起一个名称,不可以重复,实际开发中,每个方法维护一个线程池
            //每个属性都是对应的一个 HystrixProperty
            threadPoolProperties = {
                    //coreSize:线程池同时执行线程的个数,并发线程数
                    @HystrixProperty(name = "coreSize",value = "1"),
                    //maxQueueSize:启用线程队列,默认线程队列值为 -1,默认不开启
                    @HystrixProperty(name = "maxQueueSize",value = "20")
            },
            //超时时间设置
            //每个属性都是对应的一个 HystrixProperty
            commandProperties = {
                    //设置请求的超时时间,一般请求超过此时间,那么都按照超时处理,默认超时时间为 1秒
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
            }
    )             //进行熔断控制
    @GetMapping("/getProductServerPort2")
    public String getProductServerPort2(){
        //因为配置了负载均衡,所以不需要获取getInstances,直接使用微服务名称即可调用
        String url = "http://szx-service-product/service/getPort";
        System.out.println("******************************************************************************");
        System.out.println("url = " + url);
        System.out.println("******************************************************************************");
        String result = restTemplate.getForObject(url, String.class);
        return result;
    }
    
  • 商品微服务模拟超时操作

    @RequestMapping("/getPort")
    public String getPort(){
        try {
            //模拟网络延迟,休眠5秒
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return port;
    }
    

在这里插入图片描述

降级处理

配置@HystrixCommand注解,定义降级处理方法

/**
 * 服务降级演示,是在服务熔断之后的兜底操作
 * @return
 */
@HystrixCommand(//超时时间设置
        //每个属性都是对应的一个 HystrixProperty
        commandProperties = {
                //设置请求的超时时间,一般请求超过此时间,那么都按照超时处理,默认超时时间为 1秒
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
        },
        //设置超时后的回退
        fallbackMethod = "getProductServerPortFallBack"
)             //进行熔断控制
@GetMapping("/getProductServerPort3")
    public String getProductServerPort3(){
    //因为配置了负载均衡,所以不需要获取getInstances,直接使用微服务名称即可调用
    String url = "http://szx-service-product/service/getPort";
    System.out.println("******************************************************************************");
    System.out.println("url = " + url);
    System.out.println("******************************************************************************");
    String result = restTemplate.getForObject(url, String.class);
    return result;
}

/**
 * 定义回退方法,当请求触发熔断后执行,补救措施
 * 注意:
 * 1.方法形参和原方法保持一致
 * 2.方法的返回值与原方法保持一致
 * @return
 */
public String getProductServerPortFallBack(){
    return "-1";
}

Hystrix舱壁模式

即:线程池隔离策略

如果不进行任何设置,所有熔断方法使用一个Hystrix线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可用导致的,而是我们的线程机制导致的,如果方法A的请求把10个线程都用了,方法2请求处理的时候压根都没法去访问B,因为没有线程可用,并不是B服务不可用。

在这里插入图片描述

为了避免问题服务请求过多导致正常服务无法访问,Hystrix 不是采用增加线程数,而是单独的为每一个控制方法创建一个线程池的方式,这种模式叫做“舱壁模式",也是线程隔离的手段。

//只要是在 HystrixCommand 中定义了 threadPoolKey 属性,就是开启了舱壁模式(线程隔离),该方法就会自己维护一个线程池
threadPoolKey = "getProductServerPort2",         //threadPoolKey给自己维护的线程池起一个名称,不可以重复,实际开发中,每个方法维护一个线程池

舱壁模式本身就是线程池的一种隔离策略

Hystrix工作流程与高级应用

在这里插入图片描述

1)当调用出现问题时,开启一个时间窗(10s)

2)在这个时间窗内,统计调用次数是否达到最小请求数?

  • 如果没有达到,则重置统计信息,回到第1步
  • 如果达到了,则统计失败的请求数占所有请求数的百分比,是否达到阈值?
    • 如果达到,则跳闸(不再请求对应服务)
    • 如果没有达到,则重置统计信息,回到第1步

3)如果跳闸,则会开启一个活动窗口(默认5s),每隔5s,Hystrix会让一个请求通过,到达那个问题服务,看是否调用成功,如果成功,重置断路器回到第1步,如果失败,回到第3步

@HystrixCommand(//超时时间设置
        //每个属性都是对应的一个 HystrixProperty
        commandProperties = {
                //设置请求的超时时间,一般请求超过此时间,那么都按照超时处理,默认超时时间为 1秒
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000"),
                //统计窗口时间的设置,默认 10 秒
                @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "8000"),
                //统计窗口内的最小请求数
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "2"),
                //统计窗口内错误请求阈值的设置  50%
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),
                //自我修复的活动窗口时间
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "3000")
        },
        //设置超时后的回退
        fallbackMethod = "getProductServerPortFallBack"
)             //进行熔断控制

我们上述通过注解进行的配置也可以配置在配置文件中

#配置熔断策略
#强制打开熔断器,如果该属性设置为true,强制断路器进入打开状态,将会拒绝所有的请求。默认false关闭的
hystrix.command.default.circuitBreaker.forceOpen:false
#触发熔断错误比例阈值,默认值50%
hystrix.command.default.circuitBreaker.errorThresholdPercentage:50
#熔断后休眠时长,默认值5秒
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds:3000
#熔断触发最小请求次数,默认值是20
hystrix.command.default.circuitBreaker.requestVolumeThreshold:2
#熔断超时设置,默认为1秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:2000

基于springboot的健康检查观察跳闸状态(自动投递微服务暴露健康检查细节)

#springboot中暴露健康检查等断点接口
management.endpoints.web.exposure.include=*
#暴露健康接口细节
management.endpoint.health.show-details=always

访问健康检查接口:http://localhost:9100/actuator/health

在这里插入图片描述

Hystrix 线程池队列配置案例:

有一次在生产环境,突然出现了很多笔还款单被挂起,后来排查原因,发现是内部系统调用时出现了Hystrix调用异常。在开发过程中,因为核心线程数设置的比较大,没有出现这种异常。放到了测试环境,偶尔有出现这种情况。

后来调整maxQueueSize属性,确实有所改善。可没想到在生产环境跑了一段时间后却又出现这种了情况,此时我第一想法就是去查看maxQueueSize属性,可是maxQueueSize属性是设置值了。

当时就比较纳闷了,为什么maxQueueSize属性不起作用,后来通过查看官方文档发现Hystrix还有一个queueSizeRejectionThreshold属性,这个属性是控制队列最大阈值的,而Hystrix默认只配置了5个,因此就算我们把maxQueueSize的值设置再大,也是不起作用的。两个属性必须同时配置

#并发执行的最大线程数,默认10
hystrix.threadpool.default.coreSize:10
#BlockingQueue的最大队列数,默认值-1
hystrix.threadpool.default.maxQueueSize:1000
#即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5
hystrix.threadpool.default.queueSizeRejectionThreshold:800

正确的配置案例

  • 将核心线程数调低,最大队列数和队列拒绝阈值的值都设置大一点:

    #并发执行的最大线程数,默认10
    hystrix.threadpool.default.coreSize:10
    #BlockingQueue的最大队列数,默认值-1
    hystrix.threadpool.default.maxQueueSize:1500
    #即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5
    hystrix.threadpool.default.queueSizeRejectionThreshold:1000
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值