当前位置:   article > 正文

微服务系列(六) 服务熔断与服务降级_服务熔断和服务降级

服务熔断和服务降级

一. 背景

分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应。

二. 服务雪崩

微服务之间进行服务调用是由于某一个服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程。

图解雪崩效应

如存在如下调用链路:
在这里插入图片描述
而此时,Service A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算Service A能扛得住请求,Service B和Service C未必能扛得住这突发的请求。此时,如果Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,Service A也会不可用,这一过程如下图所示
在这里插入图片描述

雪崩效应常见场景
  • 硬件故障:如服务器宕机,机房断电,光纤被挖断等。
  • 流量激增:如异常流量,重试加大流量等。
  • 缓存穿透:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用。
  • 程序BUG:如程序逻辑导致内存泄漏,JVM长时间FullGC等。 同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽。
雪崩效应应对策略

针对造成雪崩效应的不同场景,可以使用不同的应对策略,没有一种通用所有场景的策略,参考如下:

  • 硬件故障:多机房容灾、异地多活等。
  • 流量激增:服务自动扩容、流量控制(限流、关闭重试)等。
  • 缓存穿透:缓存预加载、缓存异步加载等。
  • 程序BUG:修改程序bug、及时释放资源等。
  • 同步等待:资源隔离、MQ解耦、不可用服务调用快速失败等。资源隔离通常指不同服务调用采用不同的线程池;不可用服务调用快速失败一般通过熔断器模式结合超时机制实现。

综上所述,如果一个应用不能对来自依赖的故障进行隔离,那该应用本身就处在被拖垮的风险中。 因此,为了构建稳定、可靠的分布式系统,我们的服务应当具有自我保护能力,当依赖服务不可用时,当前服务启动自我保护功能,从而避免发生雪崩效应。本文将重点介绍使用Hystrix解决同步等待的雪崩问题。

三. Histrix 使用

在这里插入图片描述
In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency.      –摘自官方

译: 在分布式环境中,许多服务依赖项不可避免地会失败。Hystrix是一个库,它通过添加延迟容忍和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止它们之间的级联故障以及提供后备选项来实现这一点,所有这些都可以提高系统的整体弹性。

1. Histrix设计与实现

Hystrix [hɪst’rɪks],中文含义是豪猪,因其背上长满棘刺,从而拥有了自我保护的能力。本文所说的Hystrix是Netflix开源的一款容错框架,同样具有自我保护能力,为了实现容错和自我保护,下面我们看看Hystrix如何设计和实现的。

Hystrix设计目标
  • 对来自依赖的延迟和故障进行防护和控制——这些依赖通常都是通过网络访问的
  • 阻止故障的连锁反应
  • 快速失败并迅速恢复
  • 回退并优雅降级
  • 提供近实时的监控与告警
Hystrix遵循的设计原则
  • 防止任何单独的依赖耗尽资源(线程)
  • 过载立即切断并快速失败,防止排队
  • 尽可能提供回退以保护用户免受故障
  • 使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一个依赖的影响
  • 通过近实时的指标,监控和告警,确保故障被及时发现
  • 通过动态修改配置属性,确保故障及时恢复
  • 防止整个依赖客户端执行失败,而不仅仅是网络通信
Hystrix如何实现这些设计目标?
  • 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行;
    每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。
  • 记录请求成功,失败,超时和线程拒绝。
  • 服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
  • 请求失败,被拒绝,超时或熔断时执行降级逻辑。
  • 近实时地监控指标和配置的修改。
2. 服务熔断和服务降级
服务熔断

“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,某个异常条件被触发,直接熔断整个服务。向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。如果目标服务情况好转则恢复调用。服务熔断是解决服务雪崩的重要手段。
服务熔断图示:
在这里插入图片描述

服务降级

服务压力剧增的时候根据当前的业务情况及流量对一些服务和页面有策略的降级,以此环节服务器的压力,以保证核心任务的进行。同时保证部分甚至大部分任务客户能得到正确的相应。也就是当前的请求处理不了了或者出错了,给一个默认的返回。
通俗: 关闭系统中边缘服务 保证系统核心服务的正常运行 称之为服务降级
在这里插入图片描述

服务熔断与服务降级对比
共同点
  • 目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
  • 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
  • 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
  • 自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段。
异同点
  • 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
  • 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)。
总结

熔断必会触发降级,所以熔断也是降级一种,区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理。

3. 使用Histrix实现服务熔断
服务熔断的实现思路
  • 引入hystrix依赖,并开启熔断器(断路器)
  • 模拟降级方法
  • 进行调用测试
项目中引入hystrix依赖
<!--引入hystrix-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
开启断路器

在启动类上加入注解
@EnableCircuitBreaker //启用断路器
注意: 这里其实也可以使用 spring cloud应用中的@SpringCloudApplication注解,因为它已经自带了这些注解,源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

加入注解


@SpringBootApplication
@EnableCircuitBreaker  //用来开启断路器
public class Products9998Application {
    public static void main(String[] args) {
        SpringApplication.run(Products9998Application.class, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
使用HystrixCommand注解实现断路

//服务熔断
@GetMapping("/product/break")
@HystrixCommand(fallbackMethod = "testBreakFall" )
public String testBreak(int id){
  log.info("接收的商品id为: "+ id);
  if(id<=0){
    throw new RuntimeException("数据不合法!!!");
  }
  return "当前接收商品id: "+id;
}

public String testBreakFall(int id){
  return "当前数据不合法: "+id;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

访问测试
  • 正常参数访问
  • 错误参数访问
    在这里插入图片描述
    在这里插入图片描述
总结

从上面演示过程中会发现如果触发一定条件断路器会自动打开,过了一点时间正常之后又会关闭。那么断路器打开条件是什么呢?

断路器打开条件

A service failure in the lower level of services can cause cascading failure all the way up to the user. When calls to a particular service exceed circuitBreaker.requestVolumeThreshold (default: 20 requests) and the failure percentage is greater than circuitBreaker.errorThresholdPercentage (default: >50%) in a rolling window defined by metrics.rollingStats.timeInMilliseconds (default: 10 seconds), the circuit opens and the call is not made. In cases of error and an open circuit, a fallback can be provided by the developer.

[摘自官方]

原文翻译之后,总结打开关闭的条件:

  • 当满足一定的阀值的时候(默认10秒内超过20个请求次数)
  • 当失败率达到一定的时候(默认10秒内超过50%的请求失败)
  • 到达以上阀值,断路器将会开启
  • 当开启的时候,所有请求都不会进行转发
  • 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。
    在这里插入图片描述
默认的服务FallBack处理方法

如果为每一个服务方法开发一个降级,对于我们来说,可能会出现大量的代码的冗余,不利于维护,这个时候就需要加入默认服务降级处理方法。


package com.dp.controller;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "testBreakFall2")  //类下全局生效
public class GoodController {

    @GetMapping("/good/break")
    @HystrixCommand(fallbackMethod = "testBreakFall2") //特殊定制改接口的服务熔断方法
    public String testBreak(int id) {
        log.info("接收的商品id为: "+ id);
        if(id<=0){
            throw new RuntimeException("数据不合法!!!");
        }
        return "当前接收商品id: "+id;
    }

    @GetMapping("/goodB/break")
    @HystrixCommand  //这里不配置服务熔断方法,使用默认的处理方法
    public String testBreak2(int id) {
        log.info("接收的商品id为: "+ id);
        if(id<=0){
            throw new RuntimeException("数据不合法!!!");
        }
        return "当前接收商品id: "+id;
    }
	
	//特殊定制的熔断处理方法
    public String testBreakFall2(@RequestParam("id") int id){
        return "当前数据不合法: "+id;
    }
	
	//默认的服务熔断全局处理方法
    public String testBreakFall2(){
        return "当前数据不合法;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

测试特殊定制的服务熔断处理方法
在这里插入图片描述
测试默认的全局的服务熔断处理方法
在这里插入图片描述
可以看到一个返回了 id,一个没有返回 id。

注意:因为默认的全局服务熔断处理方法是作为一个通用方法的,不能保证参数列表中的所有参数都一样,所以必须为无参方法,否则会报错。

4. Feign 整合 Hystrix 实现服务降级

引入了feign之后,因为feign默认集成了Hystrix,所以无法像之前那样通过 @HystrixCommand注解绑定fallback方法一样实现服务降级。
feign提供了一种新方法:只需要为fegin客户端定义的接口编写一个实现类,那么这个实现类里每一个对应的重写方法,就是在feign远程调用该接口时,失败后,具体使用的降级方法。

开启 openfeign 支持服务降级
feign.hystrix.enabled=true #开启openfeign支持降级
  • 1

yml文件配置如下:

server:
  port: 9998

spring:
  application:
    name: Product9998Application

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

feign:
  hystrix:
    enabled: true  #开启openfeign支持降级
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
在 feign 客户端中加入 fallback 映射
package com.dp.feignclient;

import com.dp.fallbackclient.FallBackClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "Product9998Application", fallback = FallBackClient.class)
public interface ProductFeign {

    @GetMapping("/product/break")
    String testBreak(@RequestParam("id") int id);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
为feign客户端接口编写实现类FeignFallback
package com.dp.fallbackclient;

import com.dp.feignclient.ProductFeign;
import org.springframework.stereotype.Component;

@Component
public class FallBackClient implements ProductFeign {
	
	@Override
    public String testBreak(int id) {
        return "我是product模块的服务降级";
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意:在服务调用的feign客户端中,通过@FeignClient注解里的fallback属性来绑定其对应降级方法的实现类。

业务层代码

package com.dp.controller;

import com.dp.feignclient.ProductFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class OrderController {
    @Autowired
    ProductFeign productFeign;

    @GetMapping("/get/product")
    public String getProduct(int id) {
        String result = productFeign.testBreak(id);
        return result;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

结果
在这里插入图片描述
在这里插入图片描述
注意:如果服务端降级和客户端降级同时开启,要求服务端降级方法的返回值必须与客户端方法降级的返回值一致!!!

5. Hystrix Dashboard 断路器监控

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

作用
  • 实时监控各个Hystrix commandKey的各种指标值
  • 通过 Dashboards 的实时监控来动态修改各种配置

Hystrix Dashboard的一个主要优点是它收集了关于每个HystrixCommand的一组度量。Hystrix仪表板以高效的方式显示每个断路器的运行状况。

创建 HystrixDashboard 模块

在使用 Hystrix Dashboard 时,需要以单独工程的方式运行。以下就是 Hystrix Dashboard 的使用流程。

server:
  port: 8801
spring:
  application:
    name: HystrixDashboard-8801
  • 1
  • 2
  • 3
  • 4
  • 5
引入依赖

注意该依赖一般情况是配合 spring-boot-starter-acurator 同时使用,该模块主要用来完成对 服务的健康监控。在生产环境中,需要实时或定期监控服务的可用性。SpringBoot 的 actuator(健康监控)功能提供了很多监控所需的接口,可以对应用系统进行配置查看、相关功能统计等。

<!--引入hystrix dashboard 依赖-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
入口类中开启hystrix dashboard
package com.dp;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboard8801 {

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
启动

启动 Hystrix Dahsboard 模块项目,我们访问 http://localhost:8801/hystrix ,就能够看到 Hystrix Dashboard 豪猪哥 的监控页面了。
在这里插入图片描述
注意: 新版本 Hystrix 需要在主启动类中指定监控路径,如果没有此项操作,在项目启动后,Hystrix Dashboard 会报: Unable to connect to Command Metric Stream 这样一个错误。配置内容如下:

package com.dp;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableCircuitBreaker
//@SpringCloudApplication
public class Product9998Application {

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

    //在主启动类中指定监控路径
    @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;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

将 服务注册中心(8761端口)、服务提供方(9998端口)、Hystrix Dashboard(8801端口) 模块进行启动。然后通过 Hystrix Dashboard 8801 来监控服务端 9998。
在这里插入图片描述

需要在 Hystrix Dashboard 中填写 监控地址:http://localhost:9998/hystrix.stream,并完成图中四部曲:
在这里插入图片描述
开启监控,所以服务都启动 ok 的话,你会看到如下界面。到此处说明配置 OK。
在这里插入图片描述

四. Hystrix 停止维护

在这里插入图片描述

翻译:Hystrix(版本1.5.18)足够稳定,可以满足Netflix对我们现有应用的需求。同时,我们的重点已经转移到对应用程序的实时性能作出反应的更具适应性的实现,而不是预先配置的设置(例如,通过自适应并发限制)。对于像Hystrix这样的东西有意义的情况,我们打算继续在现有的应用程序中使用Hystrix,并在新的内部项目中利用诸如resilience4j这样的开放和活跃的项目。我们开始建议其他人也这样做。
Dashboard也被废弃。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/84126?site
推荐阅读
相关标签
  

闽ICP备14008679号