服务熔断Hystrix

服务容错的核心知识:

  雪崩效应

    在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。

    雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
  服务隔离

    顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务。
  熔断降级

    熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。

    所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值, 也可以理解为兜底。

  服务限流

    限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者部分拒绝解决等等。

Hystrix介绍:

  Hystrix 是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
    包裹请求:使用 HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。

    跳闸机制:当某服务的错误率超过一定的阈值时, Hystrix可以自动或手动跳闸,停止请求该服务一段时间。

    资源隔离: Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。

    监控: Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。

    回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。

    自我修复:断路器打开一段时间后,会自动进入 “半开”状态。

  Rest实现服务熔断

    1.配置依赖:在服务消费者 order_service 添加Hystrix的相关依赖

<!-- Hystrix的相关依赖 -->
        <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

    2.开启熔断:在启动类 OrderApplication 中添加 @EnableCircuitBreaker 注解开启对熔断器的支持。

@SpringBootApplication
// @EntityScan("com.fgy.common.domain")
// @EnableCircuitBreaker
@SpringCloudApplication
public class OrderApplication {

    /**
     * 基于Ribbon的服务调用与负载均衡
     * @return
     */
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

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

      可以看到,我们类上的注解越来越多,在微服务中,经常会引入三个注解,@SpringBootApplication @EnableDiscoveryClient(服务注册) @EnableCircuitBreaker(开启熔断)

      于是 Spring就提供了一个组合注解:@SpringCloudApplication

    3.配置熔断降级业务逻辑

/**
 * 订单控制层
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 通过订单系统,调用商品服务根据id查询商品信息
     * @param id
     * @return
     */
    @GetMapping("/buy/{id}")
    @HystrixCommand(fallbackMethod = "orderFallBack")
    public Product findById(@PathVariable("id") Long id) {
        return restTemplate.getForObject("http://service-product/product/" + id, Product.class);
    }

    /**
     * 降级方法
     * @param id
     * @return
     */
    public Product orderFallBack(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setProductName("熔断,触发降级方法");
        return product;
    }
}

      为 findById 方法编写一个回退方法orderFallBack,该方法与 findById 方法具有相同的参数与返回值类型,该方法返回一个默认的错误信息。
      在 findById 方法上,使用注解@HystrixCommand的fallbackMethod属性,指定熔断触发的降级方法是 orderFallBack 。

        因为熔断的降级逻辑方法必须跟正常逻辑方法保证: 相同的参数列表和返回值声明。
        在 findById 方法上 HystrixCommand(fallbackMethod = "orderFallBack") 用来声明一个降级逻辑的方法

    4.测试
      当 product-service 微服务正常时,浏览器访问 http://localhost:9002/order/buy/1
        

        可以正常调用服务提供者获取数据。当将商品微服务停止时继续访问

        

        此时 Hystrix配置已经生效进入熔断降级方法。

    默认的Fallback:

      我们刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以我们可以把Fallback配置加在类上,实现默认fallback

      全局的降级方法要求返回值类型要和要被熔断的方法一致,参数列表必须为空

/**
 * 订单控制层
 */
@RestController
@RequestMapping("/order")
@DefaultProperties(defaultFallback = "defaultFallBack")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 通过订单系统,调用商品服务根据id查询商品信息
     * @param id
     * @return
     */
    @GetMapping("/buy/{id}")
    // @HystrixCommand(fallbackMethod = "orderFallBack")
  @HystrixCommand
    public Product findById(@PathVariable("id") Long id) {
        return restTemplate.getForObject("http://service-product/product/" + id, Product.class);
    }

    /**
     * 统一降级方法
     *      方法没有参数
     * @return
     */
    public Product defaultFallBack() {
        Product product = new Product();
        product.setId(-2L);
        product.setProductName("熔断,触发统一降级方法");
        return product;
    }

    /**
     * 降级方法
     * @param id
     * @return
     */
    public Product orderFallBack(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setProductName("熔断,触发降级方法");
        return product;
    }
}

      

     超时设置:

      在上面测试中,我们是直接通过停止商品微服务来模拟熔断效果,因为Hystix内部默认机制是请求在超过1秒后才会返回错误信息

      可以通过配置修改这个值:

hystrix:
 command:
  default:
   execution:
    isolation:
     thread:
      timeoutInMilliseconds: 2000

  Feign 实现服务熔断

    SpringCloud Fegin默认已为Feign整合了hystrix,所以添加Feign依赖后就不用在添加hystrix,那么怎么才能让Feign的熔断机制生效呢,只要按以下步骤开发:
      1.修改application.yml,在Fegin中开启hystrix:

        在Feign中已经内置了hystrix,但是默认是关闭的,需要在工程的 application.yml 中开启对hystrix的支持

#配置Feign
feign:
  hystrix:
    enabled: true #开启hystrix

      2.编写FeignClient接口的实现类

        基于Feign实现熔断降级,需要实现自定义的ProductFeginClient接口,且实现类需要扫描到容器中(@Component),降级方法就是实现接口的方法

@Component
public class ProductFeignClientImpl implements ProductFeignClient {

    /**
     * 熔断降级的方法
     * @param id
     * @return
     */
    @Override
    public Product findById(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setProductName("熔断,触发降级方法");
        return product;
    }
}

        修改FeignClient添加hystrix熔断

/**
 * 声明需要调用的服务名称
 * @FeignClient
 *      name:服务提供者的服务名称
 *      fallback:配置熔断发生的降级方法
 *                实现类
 */
@FeignClient(name = "service-product", fallback = ProductFeignClientImpl.class)
public interface ProductFeignClient {

    /**
     * 配置需要调用的微服务接口
     */
    @GetMapping(value = "/product/{id}")
    Product findById(@PathVariable("id") Long id);
}

      3.启动测试 .........

熔断器的状态:

  熔断器有三个状态 CLOSED 、 OPEN 、 HALF_OPEN 熔断器默认关闭状态,当触发熔断后状态变更为OPEN ,在等待到指定的时间,Hystrix会放请求检测服务是否开启,这期间熔断器会变为 HALF_OPEN 半开启状态,熔断探测服务可用则继续变更为 CLOSED 关闭熔断器。

  Closed :关闭状态(断路器关闭),所有请求都正常访问。代理类维护了最近调用失败的次数,如果某次调用失败,则使失败次数加1。如果最近失败次数超过了在给定时间内允许失败的阈值,则代理类切换到断开(Open)状态。此时代理开启了一个超时时钟,当该时钟超过了该时间,则切换到半断开(Half-Open)状态。该超时时间的设定是给了系统一次机会来修正导致调用失败的错误。
  Open :打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
  Half Open :半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。

  熔断器的默认触发阈值是20次请求。休眠时间是5秒,可以通过配置修改熔断策略:

circuitBreaker.requestVolumeThreshold=5
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=5

    requestVolumeThreshold :触发熔断的最小请求次数,默认20
    errorThresholdPercentage :触发熔断的失败请求最小占比,默认50%
    sleepWindowInMilliseconds :熔断多少秒后去尝试请求

熔断器的隔离策略:

  微服务使用Hystrix熔断器实现了服务的自动降级,让微服务具备自我保护的能力,提升了系统的稳定性,也较好的解决雪崩效应。其使用方式目前支持两种策略:

    线程池隔离策略: 使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)。
    信号量隔离策略: 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
    线程池和型号量两种策略功能支持对比如下:
      

     

猜你喜欢

转载自www.cnblogs.com/roadlandscape/p/12501010.html