zoukankan      html  css  js  c++  java
  • 客户端容错保护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 注解开启对熔断器的支持。

    @SpringCloudApplication
    // @SpringBootApplication
    @EntityScan("com.fgy.common.domain")
    // @EnableCircuitBreaker
    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。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
        线程池和型号量两种策略功能支持对比如下:
          

         

  • 相关阅读:
    BZOJ 4260 Codechef REBXOR
    [SHOI2008]小约翰的游戏John
    [POI2016]Nim z utrudnieniem
    [CQOI2013]棋盘游戏
    [SDOI2016]硬币游戏
    [BZOJ3083]遥远的国度
    [Luogu3727]曼哈顿计划E
    [HihoCoder1413]Rikka with String
    [CF666E]Forensic Examination
    [BZOJ4004][JLOI2015]装备购买
  • 原文地址:https://www.cnblogs.com/roadlandscape/p/12501010.html
Copyright © 2011-2022 走看看