zoukankan      html  css  js  c++  java
  • spring cloud 入门系列四:使用Hystrix 实现断路器进行服务容错保护

    在微服务中,我们将系统拆分为很多个服务单元,各单元之间通过服务注册和订阅消费的方式进行相互依赖。但是如果有一些服务出现问题了会怎么样?

    比如说有三个服务(ABC),A调用B,B调用C。由于网络延迟或C本身代码有问题导致B迟迟得不到回应,这样B调用C的请求就会被挂起,等待。

    在高并发的访问的情况下,这些挂起的线程得不到释放,使后续的请求阻塞,最终导致B也挂掉了。依次类推,A可能也会挂掉,进而使整个系统全部崩溃。

    为了解决整个问题,Spring Cloud 使用Hystrix进行服务容错保护,包括断路器、线程隔离等一系列的保护功能,今天我们就来看下如何通过Hystrix实现断路器。

    一、什么是Spring Cloud Hystrix?什么是断路器?

    Spring Cloud Hystrix是基于Netflix的开源框架Hystrix实现的,其目的是为了通过控制那些访问远程系统、服务和第三方的节点,从而对延迟和故障提供强大的容错能力。

    断路器类似于我们家里面强电箱里面用到的漏电断路保护器,当服务单元出现故障(类似于电器发生短路),通过断路器的故障监控功能(类似于保险丝),向调用方返回一个错误响应,避免长时间等待,从而避免故障蔓延到整个系统。

    二、没有断路器的情况下,页面展示

    还记得我们前面写的spring cloud 入门系列二:使用Eureka 进行服务治理里面的三个服务(eureka/hello-service/hello-consumer)吗?我们基于这个进行实验。

    1. 启动eureka服务注册中心,端口号1111
    2. 启动hello-service服务提供者,这里我们启动两个服务,端口号分别为9090,9091
    3. 启动hello-consumer服务消费者,端口号为9999;这个时候我们多次访问http://localhost:9999/hello-consumer是没有问题的
    4. 将hello-service端口号为9091的服务关掉,再去多次访问http://localhost:9999/hello-consumer,报错了

      PS:这里说明下,为什么要多次访问,是因为我们通过ribbon实现了负载均衡,访问http://localhost:9999/hello-consumer的时候,会轮询访问hello-service的两个服务,当访问到端口号是9091的服务时才报错,访问9090的服务就不会有问题。

    三、断路器代码实现

    接下来我们看下如何进行代码实现,我们不去修改服务注册中心和服务提供者,只需要修改服务消费者hello-consumer

    1. 修改POM文件,引入Hystrix依赖
      <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.sam</groupId>
          <artifactId>hello-consumer</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>1.5.1.RELEASE</version>
          </parent>
      
          <properties>
              <javaVersion>1.8</javaVersion>
          </properties>
      
          <dependencyManagement>
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.cloud</groupId>
                      <artifactId>spring-cloud-dependencies</artifactId>
                      <version>Camden.SR6</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
              </dependencies>
      
          </dependencyManagement>
      
          <dependencies>
              <!-- 引入eureka 客户端依赖 -->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-eureka</artifactId>
              </dependency>
              <!-- 引入ribbon 依赖 ,用来实现负载均衡,我们这里只是使用先不作其他介绍 -->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-ribbon</artifactId>
              </dependency>
              <!-- 引入hystrix 依赖 ,用来实现服务容错保护-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-hystrix</artifactId>
              </dependency>
      
          </dependencies>
      </project>
    2. 修改启动类,追加注解@EnableCircuitBreaker,开启断路器
      @EnableDiscoveryClient
      @SpringBootApplication
      @EnableCircuitBreaker
      public class ConsumerApp {
      
      
          //@Bean 应用在方法上,用来将方法返回值设为为bean
          @Bean
          @LoadBalanced  //@LoadBalanced实现负载均衡
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
          
          public static void main(String[] args) {
              SpringApplication.run(ConsumerApp.class, args);
          }
      }

      这个时候你会发现,这个启动类加了三个注解,这个是不是很麻烦?没关系,我们可以使用注解@SpringCloudApplication

      @SpringCloudApplication
      public class ConsumerApp {
      
      
          //@Bean 应用在方法上,用来将方法返回值设为为bean
          @Bean
          @LoadBalanced  //@LoadBalanced实现负载均衡
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
          
          public static void main(String[] args) {
              SpringApplication.run(ConsumerApp.class, args);
          }
      }

      @SpringCloudApplication = @EnableDiscoveryClient +@SpringBootApplication+@EnableCircuitBreaker,从源码就能看出来:

      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Inherited
      @SpringBootApplication
      @EnableDiscoveryClient
      @EnableCircuitBreaker
      public @interface SpringCloudApplication {
      }
    3. 追加service
      @Service
      public class ConsumerService {
          
          @Autowired
          RestTemplate restTemplate;
      
          @HystrixCommand(fallbackMethod = "errorMsg")
          public String consumer() {
              // 调用hello-service服务,注意这里用的是服务名,而不是具体的ip+port
              restTemplate.getForObject("http://hello-service/hello", String.class);
              return "hello consumer finish !!!";
          }
      
          public String errorMsg() {
              return "error!!!";
          }
      }

      我们把原来controller里面的调用RestTemplate的实现放到service里面,并且通过@HystrixCommand来指定回调方法,当出现错误时调用该方法。

    4. 修改controller
      /**
       *这里不再直接调用restTemplate,
       *而是通过调用service进行实现 
       *
       */
      @RestController
      public class ConsumerController {
      
          @Autowired
      //    RestTemplate restTemplate;
          ConsumerService service;
          
          
          @RequestMapping("/hello-consumer")
          public String helloConsumer() {
      //        //调用hello-service服务,注意这里用的是服务名,而不是具体的ip+port
      //        restTemplate.getForObject("http://hello-service/hello", String.class);
              return service.consumer();
          }
      }
    5. 测试,多次访问,当报错的时候,会显示如下内容

    大功告成!

  • 相关阅读:
    [书目20110210]信息系统项目管理师教程(第2版)目录
    [文摘2010220]投资人不可不知的股市法则
    生活是自我的选择和过滤
    [书目20110210]系统架构设计师教程 目录
    [转]6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
    [书目]给总经理的101条忠告
    项目管理工具 之 microsoft project help
    [文摘20110216]职业经理人必备十素质
    [文摘20110226]于丹的一百一十句人生忠告
    学习需要用心 规划和落实
  • 原文地址:https://www.cnblogs.com/sam-uncle/p/8972281.html
Copyright © 2011-2022 走看看