zoukankan      html  css  js  c++  java
  • SpringCloud系列十五:使用Hystrix实现容错

    1. 回顾

      上文讲解了容错的重要性,以及容错需要实现的功能。

      本文来讲解使用Hystrix实现容错。

    2. Hystrix简介

      Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提高系统的可用性与容错性。

      Hystrix主要通过以下几点实现延迟和容错:

    • 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。

        这使用到了设计模式中的“命令模式”。

    • 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
    • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,

        而不是排队等候,从而加速失败判定。

    • 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
    • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
    • 自我修复:断路器打开一段时间后,会自动进入“半开”状态。断路器打开、关闭、半开的逻辑转换,上文已经详细探讨过,不再赘述。

    3. 通用方式整合Hystrix

      > 复制项目 microservice-consumer-movie-ribbon,将 ArtifactId 修改为 microservice-consumer-movie-ribbon-hystrix

      > 添加 hystrix 的依赖

    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>

      > 在启动类上添加 @EnableCircuitBreaker 或 @EnableHystrix 注解,从而为项目启用断路器支持

    package com.itmuch.cloud.microserviceconsumermovieribbonhystrix;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableHystrix
    public class MicroserviceConsumerMovieRibbonHystrixApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MicroserviceConsumerMovieRibbonHystrixApplication.class, args);
        }
    
        @Bean
        @LoadBalanced // 实现负载均衡
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }

      > 修改 MovieController,让其中的findById方法具备容错能力。

    package com.itmuch.cloud.microserviceconsumermovieribbonhystrix.controller;
    
    import com.itmuch.cloud.microserviceconsumermovieribbonhystrix.pojo.User;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    public class MovieController {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
    
        @Autowired
        private RestTemplate restTemplate;
        @Autowired
        private LoadBalancerClient loadBalancerClient;
    
        @HystrixCommand(fallbackMethod = "findByIdFallback")
        @GetMapping("/user/{id}")
        public User findById(@PathVariable Long id) {
            return this.restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
        }
    
        @GetMapping("/log-instance")
        public void logUserInstance() {
            ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
            // 打印当前选择的是哪个节点
            MovieController.LOGGER.info("{}:{}:{}", serviceInstance.getServiceId(), serviceInstance.getHost(),
                    serviceInstance.getPort());
        }
    
        public User findByIdFallback(Long id) {
            LOGGER.warn("请求异常,执行回退方式");
            User user = new User();
            user.setId(-1L);
            user.setName("默认用户");
            return user;
        }
    
    }

      > 启动项目 microservice-discovery-eureka

      > 启动项目 microservice-provider-user

      > 启动项目 microservice-consumer-movie-ribbon-hystrix

      > 访问 http://localhost:8010/user/1,获得如下结果,说明可正常访问

      > 停止项目 microservice-provider-user

      > 访问 http://localhost:8010/user/1,获得如下结果,说明当用户微服务不可用时,进入了回退方法

      注意:当请求失败、被拒绝、超时或者断路器打开时,都会进入回退方法。但进入回退方法并不意味着断路器已被打开。

    4. Hystrix断路器的状态监控与深入理解

      还记得之前已经为项目引入了Spring Boot Actuator 的依赖,该依赖的作用就是监控应用程序的运行状态。

    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

      断路器的状态也会暴露在Actuator提供的 /actuator/health 端口中,这样就可以直观地了解断路器的状态。

      但是之前使用的是 Actuator 的默认配置。在默认配置中,/actuator/health 端口只能查看系统的状态,不能查看详细信息,

      因此需要对 Actuator进行进一步配置。

      在 application.yml 中添加如下配置

    management:
      endpoint:
        health:
          show-details: always

      > 启动项目 microservice-discovery-eureka

      > 启动项目 microservice-consumer-movie-ribbon-hystrix

      > 访问 http://localhost:8010/user/1,结果如下,说明已执行了回退逻辑,返回了默认用户。

      > 访问 http://localhost:8010/actuator/health,结果如下,此时Hystrix的状态依然是 UP,这是因为我们的失败率还没有达到阈值

        (默认是5秒返回20次失败)

      > 持续快速的访问 http://localhost:8010/user/1,然后访问 http://localhost:8010/actuator/health,此时结果如下

     

      注意:由上面的测试可以发现——执行回退逻辑并不代表断路器已经打开。请求失败、超时、被拒绝以及断路器打开时都会执行回退逻辑。

    5. Hystrix线程隔离策略与传播上下文

      先来阅读一下Hystrix官方Wiki(https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy

      执行

        以下属性控制 HystrixCommand.run() 是如何运行的

      execution.isolation.strategy

        这个属性决定了 HystrixCommand.run() 以哪种隔离策略去执行,下面两种选择中的一个:

      • THREAD(线程隔离):它将在单独的线程上执行,并发请求受线程池中的线程数量的限制。
      • SEMAPHORE(信号量隔离):它将在调用线程上执行,开销相对较小,并发请求收到信号量个数的限制。

      Thread or Semaphore

        默认并且推荐的设置是,运行 HystrixCommand 使用线程隔离(THREAD)HystrixObservableCommand 使用信号量隔离(SEMAPHORE)

        在线程中执行命令有一个除网络超时以外的额外保护层。

        一般来说,只有当调用负载非常高时(每个实例每秒调用数百次)才需要对 HystrixCommand 使用信号量隔离,因为这种场景下

        使用单独线程开销太高,这通常只适用于非网络调用。

      

     

      了解了Hystrix的隔离策略后,再来看一下 Spring Cloud 官方的文档(http://cloud.spring.io/spring-cloud-static/Finchley.M8/single/spring-cloud.html#_propagating_the_security_context_or_using_spring_scopes

      如果你想传播线程本地的上下文到 @HystrixCommand,默认声明将不会工作,因为它会在线程池中执行命令(在超时的情况下)。

      你可以使用一些配置,让 Hystrix 使用相同的线程,或者直接在注解中让 Hystrix 使用不同的隔离策略。例如:

    @HystrixCommand(fallbackMethod = "stubMyService",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
        }
    )
    ...

      这也适用于使用 @SessionScope 和 @RequestSession 的情况。你会知道什么时候需要这么做,因为会发生一个运行时异常,

      说它找不到作用域上下文(scoped context)。

      你还可以将 hystrix.shareSecurityContext 属性设置为 true,这样将会自动配置一个 Hystrix 并发策略插件的 hook,这个hook会

      将 SecurityContext 从主线程传输到 Hystrix 的命令。因为 Hystrix 不允许注册多个 Hystrix 策略,所以可以声明 HystrixConcurrencyStrategy

      为一个Spring bean来实现扩展。Spring Cloud会在Spring的上下文中查找你的实现,并将其包装在自己的插件中。

      把Spring Cloud 和 Hystrix 的文档对照阅读,可以帮助理解相关概念。

    • Hystrix的隔离策略有THREAD和SEMAPHORE两种,默认是THREAD。
    • 正常情况下,保持默认即可。
    • 如果发生找不到上下文的运行时异常,可考虑将隔离策略设置为SEMAPHORE。

    6. 总结

      本文讲解了Hystrix的整合,对Hystrix断路器的理解及监控,还有Hystrix的隔离策略与传播上下文等。

      下文将讲解Feign使用Hystrix。敬请期待~~~

    7. 参考

      周立 --- 《Spring Cloud与Docker微服务架构与实战》

  • 相关阅读:
    mysql中txt的导入及导出
    邮箱手机正则
    用yum安装完mysql后没有mysqld的问题
    利用php在图片上写字(中英文)
    修改MYSQL密码及权限
    mysql 索引!
    MySQL对表的复制,修改及删除
    防止表单重复提交!
    Linux学习篇SVN命令详解
    Linux 安装PHP配置参数
  • 原文地址:https://www.cnblogs.com/jinjiyese153/p/8669702.html
Copyright © 2011-2022 走看看