SpringCloud之服务熔断

雪崩效应(熔断器的背景)

    在微服务架构中,一个应用由多个服务组成。相互依赖,依赖关系错综复杂。若有一个服务因为故障原因,可能会导致整个服务崩溃。

    例如一个微服务架构存在A、B、C、D、E、F等多个服务,它们的依赖关系如下图。
在这里插入图片描述

图1:服务依赖关系

    请求1调用A、D、E、F四个服务才能完成。请求 2 需要调用 B、E、D 三个服务才能完成,请求 3 需要调用服务 C、F、E、D 四个服务才能完成。微服务之间的调用,就是所谓的扇出,当扇出的链路上服务E发生了故障后者网络验证,会出现以下情况:

  1. 即使其他所有服务都可用,由于服务E不可用,那么用户请求1,2,3都会处于阻塞状态,在高并发的环境下,会导致服务器资源的线程资源短时间耗尽。
  2. 所有的依赖于服务E的其他服务,例如服务 B、D 以及 F 也都会处于线程阻塞状态,会处于等待服务E的响应状态,导致这些服务不可用。
  3. 所有依赖服务B、D 和 F 的服务,例如服务 A 和服务 C 也会处于线程阻塞状态,以等待服务 D 和服务 F 的响应,导致服务 A 和服务 C 也不可用。

    通过上面可以看出来,微服务系统因为一个服务出现故障,而导致故障沿着服务调用而疯狂蔓延,进而导致整个微服务系统瘫痪,这就是"雪崩效应",为了防止此类事件发生,微服务架构引入了"熔断器"的一些列的服务容错和保护措施。

熔断器简介

    物理学电路知识涉及到熔断器(Circuit Breaker)的概念,当电路发生故障时,会迅速切断电源来保护电路的安全

    在微服务领域上,Martin Fowler也提出了熔断器这一知识,与物理学的熔断器相似,在微服务架构中有些微服务由于网络问题或服务故障的原因,熔断器及时作出向服务调用方返回一个符合预期的,可处理的降级响应(FallBack),而不是让用户长时间的等待或者抛出用户无法处理的异常,这样保证了服务提供方不会对系统资源长时间的,不必要的占用,避免故障在微服务系统中的蔓延,防止雪崩效应的产生。

Spring Cloud Hystrix

Hystrix简介

    Spring Cloud Hystrix是一款优秀的服务容错和保护组件,也是Spring Cloud的重要组件之一。

    Spring Cloud Hystrix是基于Netfilx公司的开源组件Hystrix实现的。提供熔断器功能,能够有效阻止分布式服务系统中出现联动故障,以提高微服务系统的弹性。Spring Cloud Hystrix具有服务降级,服务熔断,线程隔离,请求缓存,请求合并以及实时故障监控等强大功能。

	    Hystrix [hɪst'rɪks],中文含义是豪猪,豪猪的背上长满了棘刺,使它拥有了强大的自我保护能力。
		而 Spring Cloud Hystrix 作为一个服务容错与保护组件,也可以让服务拥有自我保护的能力,
		因此也有人将其戏称为“豪猪哥”。

Hystrix能做什么

  • 保护线程资源:防止单个服务的故障耗尽系统中所有的线程资源。
  • 快速失败机制:当某个服务发生了故障,不让服务调用方一直等待,而直接返回一个可预期的,可处理的降级响应。
  • 提供降级(FallBack)方案:在请求失败后,提供一个设计好的降级方案,当请求失败后掉员工此方法。
  • 防止故障扩散:使用熔断机制,防止故障扩散到其他服务。
  • 提供熔断器故障监控组件Hystrix Dashboard,随时监控熔断器的状态。

请求,服务正常情况下:

在这里插入图片描述

出现服务故障时,它可以阻止整个用户请求

在这里插入图片描述
随着大容量通信的增加,某个服务的故障会导致所有服务器上的线程资源短时间内饱和。

应用程序中通过网络或客户端库可能导致网络请求的每个点都是潜在故障的来源。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,从而备份队列、线程和其他系统资源,从而导致更多跨系统的级联故障,引起雪崩现象。

在这里插入图片描述
当使用Hystrix包装每个基础依赖项时,上面的图表中所示的体系结构会发生类似于以下关系图的变化。每个依赖项是相互隔离的,限制在延迟发生时它可以填充的资源中,并包含在回退逻辑中,该逻辑决定在依赖项中发生任何类型的故障时要做出什么样的响应

在这里插入图片描述

Hystrix服务熔断

熔断机制是为了应对雪崩效应而出现一种微服务链路保护机制。

当微服务系统的某个微服务不可用或响应时间太长时,为了保护系统的服务整体可用性,熔断器会暂时切断对该服务的请求调用,并快速返回一个友好的错误响应信息。这种熔断状态不是永久的,在经历了一定时间后,熔断器会再次检测该微服务是否恢复正常,若恢复正常则恢复调用链路。

熔断状态

在这里插入图片描述

  • 熔断关闭状态(Closed):当服务访问正常时,熔断器处于关闭状态,服务调用方可以正常地对服务进行调用。
  • 熔断开启状态(Open):默认情况下,在固定时间内接口调用出错比率达到一个阈值(例如 50%),熔断器会进入熔断开启状态。进入熔断状态后,后续对该服务的调用都会被切断,熔断器会执行本地的降级(FallBack)方法。
  • 半熔断状态(Half-Open): 在熔断开启一段时间之后,熔断器会进入半熔断状态。在半熔断状态下,熔断器会尝试恢复服务调用方对服务的调用,允许部分请求调用该服务,并监控其调用成功率。如果成功率达到预期,则说明服务已恢复正常,熔断器进入关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。

Hystrix实现熔断机制

在SpringCLoud中,熔断机制是通过Hystrix实现的。Hystrix会监控微服务间调用的情况,当失败调用达到一定比例时(例如5秒内失败20次),就会启动熔断机制。

  1. 当服务的调用出错率达到后超过Hystirx规定的出错比率(默认为50%),熔断器进入熔断开启状态。
  2. 熔断器进入熔断开启状态后,Hystrix会启动一个休眠时间窗,在这个时间窗内,该服务的降级逻辑会临时充当业务主逻辑,而原来的业务主逻辑不可用。
  3. 当有请求再次调用该服务时,会直接调用降级逻辑快递返回失败效应,以避免系统雪崩。
  4. 当休眠时间窗到期后,Hystrix会进入半熔断状态,允许部分请求对由原来的业务主逻辑进行调用,并监控其成功率。
  5. 如果调用成功率达到预期,说明服务恢复正常,Hystrix进入熔断关闭状态,服务原来的业务逻辑恢复。否则Hystrix将进入熔断开启状态,休眠时间窗口重新计时。

Hystrix熔断实现

1.在原来创建的模块中,创建一个名叫springcloud-user-provider-hystrix-8081的子模块,并拷贝springcloud-user-provider-8081模块的pom.xml、resource下的文件和Java代码进行初始化并调整。

2.在pom.xml导入Hystrix依赖

<!--导入Hystrix依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

3.修改application.yml

spring:
  application:
    name: springcloud-user-provider-hystrix
  datasource: # 连接数据源配置
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
    username: root
    password: root

server:
  port: 8081

#mybatis配置
mybatis:
  type-aliases-package: org.liang.pojo
  mapper-locations: mapper/*.xml


# Spring Boot 2.50对 actuator 监控屏蔽了大多数的节点,只暴露了 heath 节点,本段配置(*)就是为了开启所有的节点
management:
  endpoints:
    web:
      exposure:
        include: "*"   # * 在yaml 文件属于关键字,所以需要加引号


# 注册服务到eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  # 配置eureka上服务的默认描述信息
  instance:
    instance-id: springcloud-user-provider-hystrix

  # 显示访问路径的ip地址
    prefer-ip-address: true

  #info配置服务的具体描述信息
info:
  app.name: springcloud-provider
  funcation: description service

prefer-ip-address设置为true,eureka监控显示的就是ip地址,而不是localhost了

management.endpoints-web-exposure-include 控制监控暴露的节点

3.修改Controller

其实Controller或者Service都可以层都可以。添加如下代码。

//    调用错误的Id,抛出异常,熔断就会调用@HystrixCommand注解标注的fallbackMethod(回调方法)指定的方法
    @HystrixCommand(fallbackMethod = "circuit")
    public User querySingleUser(@PathVariable("id") Integer id) {

        if(null == id)
        {
            throw new RuntimeException("不存在"+id+"用户,或信息找不到");
        }
        //模拟一下
         User user = userService.getUserById(id);
         user.setAlias("8081");
        return user;
    }


    /**
     * 备选方案,降级方法
     * @return
     */
    public User circuit(@PathVariable("id")Integer id)
    {
        return new User().setId(id).setUsername(id+"没有找到对应信息,null--@Hystrix").setAlias(null);
    }


4,启动测试

访问正常

在这里插入图片描述
访问失败
在这里插入图片描述

5.服务响应时间过长的情况

服务因为网络问题或者不可用时,会暂时切断该服务的请求调用,执行该服务的降级逻辑。现在模拟超时的情况。

在Service层修改代码

  public List<User> timeout()
    {
        List<User> users =new ArrayList<>();
        users.add(new User().setUsername("系统繁忙,请稍后再试"));
        return users;
    }



    @HystrixCommand(fallbackMethod = "timeout",commandProperties =
            {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")})
//    或者响应时间太长,抛出异常,熔断就会调用@HystrixCommand注解标注的fallbackMethod(回调方法)指定的方法
    @Override
    public List<User> getUsers() {
        int timeout = 6;
        try
        {
            TimeUnit.SECONDS.sleep(timeout);
        }catch (Exception e)
        {
            throw new RuntimeException("出错了");
        }
        return userMapper.getUsers();
    }

6.启动测试
报
出现调用服务超时的错误,这是熔断器经过发送http请求尝试连接但未成功返回错误信息,确保没有开启服务降级,在springcloud-user-feign-80模块的配置文件加上负载均衡时间配置】。

7.springcloud-user-feign-80模块的application.yml

ribbon:
ReadTimeOut: 6000
ConnectTimeOut: 6000

8.重启测试
在这里插入图片描述

HystrixProperty配置

参数描述
execution.isolation.thread.timeoutInMilliseconds为请求方法设置超时时间,若请求超时则触发全局的回退方法进行处理。可以用在某个特定的服务请求中
metrics.rollingStats.timeInMilliseconds统计时间窗。
circuitBreaker.sleepWindowInMilliseconds休眠时间窗,熔断开启状态持续一段时间后,熔断器会自动进入半熔断状态,这段时间就被称为休眠窗口期。
circuitBreaker.requestVolumeThreshold请求总数阀值。在统计时间窗内,请求总数必须到达一定的数量级,Hystrix 才可能会将熔断器打开进入熔断开启转态,而这个请求数量级就是 请求总数阀值。Hystrix 请求总数阈值默认为 20,这就意味着在统计时间窗内,如果服务调用次数不足 20 次,即使所有的请求都调用出错,熔断器也不会打开。
circuitBreaker.errorThresholdPercentage错误百分比阈值。当请求总数在统计时间窗内超过了请求总数阀值,且请求调用出错率超过一定的比例,熔断器才会打开进入熔断开启转态,而这个比例就是错误百分比阈值。错误百分比阈值设置为 50,就表示错误百分比为 50%,如果服务发生了 30 次调用,其中有 15 次发生了错误,即超过了 50% 的错误百分比,这时候将熔断器就会打开。

代码实现

在springcloud-user-provider-hystrix-8081中service层添加以下代码,记得把controller的服务熔断方法注释掉

1.springcloud-user-provider-hystrix-8081的UserService

 @HystrixCommand(fallbackMethod = "prop",commandProperties =
            {
                    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //开启熔断器
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value ="1000"), //统计时间窗
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),      //统计时间窗内请求次数
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //休眠时间窗口期
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), 在统计时间窗口期以内,请求失败率达到 60% 时进入熔断状态

            })

    @Override
    public User getUserById(int id) {
        if(id<0)
        {
            throw new RuntimeException("ID为负数,用户ID不存在");
        }
        User user  = userMapper.getUserById(id);
        if(null == user)
        {
            throw new RuntimeException("该用户不存在");
        }
        return user;
    }

    public User prop(int id)
    {
        return new User().setId(id).setUsername("用户ID不存在,请您确认好信息").setAlias(null);
    }


2.启动测试

可以发现服务熔断已经实现

在这里插入图片描述

Hystrix服务降级

服务降级是指当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或者以简单的方式处理,从而释放服务器资源保障核心业务正常运作或高效运作,简而言之:就是尽可能的把系统资源让给优先级高的服务。

使用场景:

  • 当服务器压力剧增时,根据实际业务情况及流量,对一些不重要的、不紧要的服务进行有策略地不处理或者简单处理,从而释放服务器资源以保障核心业务正常运作。
  • 当某些服务不可用时,为了避免长时间服务卡顿或者引起雪崩效应,而主动执性备用的降级逻辑立刻返回一个有好多提示,以保证主体业务不受影响。

实现:

通过重写 HystrixCommand 的 getFallBack() 方法或 HystrixObservableCommand 的 resumeWithFallback() 方法,使服务支持服务降级。

某一时间段内服务A的访问量保证,而B和C的访问量较少或者访问量几乎没有,为了缓解A服务的压力,这时候把B和C的服务功能暂时关闭,释放服务资源去承担A的部分服务,重而分担了A的压力,提高了性能,叫做服务降级。

服务降级需要考虑的问题

1.哪些服务是核心服务,哪些服务是非核心服务
2.哪些服务支持降级,哪些服务不支持,降级策略是什么?
3.在服务降级之外是否存在更复杂的业务场景,策略是什么?

使用场景:

  • 超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况

  • 失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况

  • 故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)

  • 限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。

服务降级实现

在springcloud-user-api模块中进行服务降级

1.实现降级类serServiceFallbackFactory

//实现FallbackFactory,来实现服务降级,客户端
@Component	//加入到容器中
public class UserServiceFallbackFactory implements FallbackFactory {

    //重写服务,来实现服务降级
    @Override
    public UserService create(Throwable throwable)
    {
        return new UserService() {
            @Override
            public boolean addUser(User user) {
                return false;
            }

            @Override
            public List<User> query() {
                return null;
            }

            @Override
            public User querySingleUser(int id) {
                return new User().setId(id).setUsername("id为"+id+" 未找到对应的信息,该服务已经关闭了")
                        .setAlias(null);
            }
        };
    }
}

2.在UserService指定降级策略

@FeignClient(value = "SPRINGCLOUD-USER-PROVIDER-HYSTRIX",fallbackFactory = UserServiceFallbackFactory.class)
@Service   //加入到Spring容器中
public interface UserService{
    @PostMapping("/user/add")
    public boolean addUser(User user);

    @GetMapping("/user/query")
    public List<User> query();

    @GetMapping("/user/query/{id}")
    public User querySingleUser(@PathVariable("id")int id);


}

3.在springcloud-user-feign-80模块中配置文件中

feign:
  hystrix:
    enabled: true  #开启hystrix服务降级

4.测试
在这里插入图片描述

未找到该用户,抛出异常,立即使用服务降级的方法,给服务调用方一个服务不可用提示。

全局降级

通过上面的方式实现服务降级时,需要针对所有业务方法都配置降级方法,这极有可能会造成代码的急剧膨胀,为了解决这个问题,我们可以为所有业务方法指定一个全局的回退方法。

//@DefaultProperties(defaultFallback = "全局的服务降级方法")

写一个全局的降级方法就可以了,但要求在同一个类里

注意:降级(FallBack)方法必须与其对应的业务方法在同一个类中,否则无法生效。

若要方法实现全局降级的方法,只需要写@HystrixCommand注解即可,不需要写降级方法,就可以调用默认的降级方法。

服务熔断和服务降级的区别

1.服务熔断–>服务端:某个服务超时或异常,引起熔断。
2.服务降级–>服务端: 从整体网站考虑,当某个服务熔断或关闭之后,服务不再调用,此时客户端实现了FallBackFactory的类,返回一个用户指示信息,但会导致整体服务水平下降,但提高了可靠性。
3.触发原因不同。服务熔断一般是某个服务故障引起的,而服务降级是从整体负荷考虑,管理目标层次不太一样,熔断起始是一个框架处理,每个微服务都需要,而降级一般需要对业务有层级之分。
4.实现方式不一样。服务降级具有代码入侵性(由控制器完成/或自动降级),熔断一般称为自我熔断。

限流: 限制并发时的请求访问量,超过阈值则拒绝;
降级: 服务优先级,牺牲非核心服务(不可用),保证核心服务稳定,从整体负荷上考虑;
熔断: 依赖的下游服务故障触发熔断,避免引发本系统崩溃,系统自动执行和恢复。

Hystrix故障监控

Hystrix提供实时的服务调用监控功能,Hystrix会记录调用执行的情况信息,并以统计报表的形式展示给用户,包括每秒执行请求的数量,成功请求的数量失败请求的数量

实现

1.新建立pringcloud-user-hystrix-dashboard-9090模块

2.导入依赖

<!--Hystrix依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--dashboard依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--实体类+web-->
<dependency>
    <groupId>com.haust</groupId>
    <artifactId>springcloud-api</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-devtools</artifactId>
</dependency>

3.配置类配置端口号为9090即可
4.配置主启动类

@SpringBootApplication
@EnableHystrixDashboard //开启Hystrix监控
public class DashBoard_9090 {
    public static void main(String[] args) {
        SpringApplication.run(DashBoard_9090.class,args);
    }
}

5.springcloud-user-provider-hystrix-8081在创建配置类DashBoardConfig

@Configuration
public class DashBoardConfig {

    //设置监控页面的访问路径
    @Bean
    //添加一个ServletRegistrationBean
    public ServletRegistrationBean hystrixMetricsStreamServlet()
    {
        //创建需要监控的是hystrix,HystrixMetricsStreamServlet是监控Hystrix的类
        HystrixMetricsStreamServlet servlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean bean = new ServletRegistrationBean(servlet);
        bean.setLoadOnStartup(1);   //设置启动级别
        bean.addUrlMappings("/actuator/hystrix.stream");
        bean.setName("hystrix.stream");
        return bean;
    }
}

6.启动访问http://localhost:9001/hystrix
在这里插入图片描述

输入http://localhost:8081/actuator/hystrix.stream

在这里插入图片描述
说明:
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐