zoukankan      html  css  js  c++  java
  • 7. 使用Hystrix实现微服务的容错处理

                  使用Hystrix实现微服务的容错处理

    7.1. 实现容错的手段

      7.1.1. 雪崩效应

                       在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。

                       服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。

     

     7.1.2. 如何容错

    为了防止雪崩效应,必须有一个强大的容错机制。该容错机制需实现以下两点:

           1.为网络请求设置超时

             必须为网络请求设置超时。

              正常情况下,一个远程调用一般在及时毫秒内就能得到响应了。如果依赖的服务不可用或者网络有问题,那么响应时间就会变得特别长。

              通常情况下,一次远程调用对应着一个线程/进程。如果响应太慢,这个线程/进程就得不到释放。而线程/进程又对应着系统资源,如果得不到释放的线程/进程约积越多,资源就会逐渐被耗尽,最终导致服务的不可用。

    2.使用断路器模式

              如果对某个微服务的请求有大量超时(常常说明该微服务不可用),再去让新的请求访问该服务已经没有任何意义,只会无所谓消耗资源。

              例如,设置了超时时间为1秒,如果短时间内有大量的请求无法在1秒内得到响应,就没有必要再去请求依赖的服务了。

              断路器可理解为对容易导致错误的操作的代理。

              这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回。

              断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。

              这样,应用程序就无需再浪费cpu时间去等待长时间的超时。

              断路器也可自动诊断是否已经恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。

              使用这种方式,就可以实现微服务的“自我修复”——当依赖的服务不正常打开断路器时快速失败,从而防止雪崩效应;

              当发现依赖的服务恢复正常时,又会恢复请求。

       

             断路器状态转换逻辑:

                 - 正常情况下,断路器关闭,可正常请求依赖的服务

                 - 当一段时间内,请求失败率达到一定阀值(例如错误率达到50%,或100次/分钟等),断路器就会打开。此时,不会再去请求依赖的服务。

                 - 断路器打开一段时间后,会自动进入“半开”状态。此时,断路器可允许一个请求访问依赖的服务。如果该请求能够调用成功,则关闭断路器;否则继续保持打开状态。

     7.1.3. 熔断器

                熔断器的原理很简单,如同电力过载保护器。

                它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,

                不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,

                或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

                熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。

    7.2. 使用Hystrix实现容错

     7.2.1. Hystrix简介

       Hystrix是一个实现了超时机制和断路器模式的工具类库。

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

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

    • 包裹请求:使用HystrixCommand(或者HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
    • 跳闸机制:当某服务的错误率超过一定阀值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
    • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判断
    • 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
    • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
    • 自我修复:断路器打开一段时间后,会自动进入“半开”状态。

     7.2.2. 通用方式整合Hystrix(Ribbon使用Hystrix)

     7.2.2.1. Ribbon添加回退

            pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <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>
    
    	<artifactId>microservice-consumer-movie-ribbon-with-hystrix</artifactId>
    	<packaging>jar</packaging>
    
    	<parent>
    		<groupId>com.itmuch.cloud</groupId>
    		<artifactId>microservice-spring-cloud</artifactId>
    		<version>0.0.1-SNAPSHOT</version>
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-actuator</artifactId>
    		</dependency>
    		
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-hystrix</artifactId>
    		</dependency>
    	</dependencies>
    </project>
    

      配置文件:

    spring:
      application:
        name: microservice-consumer-movie-ribbon-with-hystrix
    server:
      port: 8010
    eureka:
      client:
        healthcheck:
          enabled: true
        serviceUrl:
          defaultZone: http://user:password123@localhost:8761/eureka
      instance:
        prefer-ip-address: true
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
    

      启动类

    package com.itmuch.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableCircuitBreaker
    public class ConsumerMovieRibbonApplication {
    
      @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
        return new RestTemplate();
      }
    
      public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
      }
    }
    

      业务类:

    package com.itmuch.cloud.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    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;
    
    import com.itmuch.cloud.entity.User;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    
    @RestController
    public class MovieController {
      @Autowired
      private RestTemplate restTemplate;
    
      @GetMapping("/movie/{id}")
      @HystrixCommand(fallbackMethod = "findByIdFallback")
      public User findById(@PathVariable Long id) {
        return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
      }
    
      public User findByIdFallback(Long id) {
        User user = new User();
        user.setId(0L);
        return user;
      }
    }
    

      实体类:

    package com.itmuch.cloud.entity;
    
    import java.math.BigDecimal;
    
    public class User {
      private Long id;
    
      private String username;
    
      private String name;
    
      private Short age;
    
      private BigDecimal balance;
    
      public Long getId() {
        return this.id;
      }
    
      public void setId(Long id) {
        this.id = id;
      }
    
      public String getUsername() {
        return this.username;
      }
    
      public void setUsername(String username) {
        this.username = username;
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    
      public Short getAge() {
        return this.age;
      }
    
      public void setAge(Short age) {
        this.age = age;
      }
    
      public BigDecimal getBalance() {
        return this.balance;
      }
    
      public void setBalance(BigDecimal balance) {
        this.balance = balance;
      }
    
    }
    

      测试:

    1 启动eureka
    2 启动user微服务
    3 启动movie微服务
    4 访问http://localhost:8010/user/1,

    结果如下

    {"id":1,"username":"user1","name":"张三","age":20,"balance":100.00}

    5 停止user微服务
    6 再次访问http://localhost:8010/user/1,

    结果如下

    {"id":0,"username":null,"name":null,"age":null,"balance":null}


    说明当前微服务不可用,进入回退方法。

    7.2.2.2. Ribbon的propagation

        pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <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>
    
    	<artifactId>microservice-consumer-movie-ribbon-with-hystrix-propagation</artifactId>
    	<packaging>jar</packaging>
    
    	<parent>
    		<groupId>com.itmuch.cloud</groupId>
    		<artifactId>microservice-spring-cloud</artifactId>
    		<version>0.0.1-SNAPSHOT</version>
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-actuator</artifactId>
    		</dependency>
    		
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-hystrix</artifactId>
    		</dependency>
    	</dependencies>
    </project>
    

      配置文件:

    spring:
      application:
        name: microservice-consumer-movie-ribbon-with-hystrix-propagation
    server:
      port: 8010
    eureka:
      client:
        healthcheck:
          enabled: true
        serviceUrl:
          defaultZone: http://user:password123@localhost:8761/eureka
      instance:
        prefer-ip-address: true
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
    

      启动类:

    package com.itmuch.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableCircuitBreaker
    public class ConsumerMovieRibbonApplication {
    
      @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
        return new RestTemplate();
      }
    
      public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
      }
    }
    

      业务类:

    package com.itmuch.cloud.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    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;
    
    import com.itmuch.cloud.entity.User;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
    
    @RestController
    public class MovieController {
      @Autowired
      private RestTemplate restTemplate;
    //表示@HystrixCommand与findById方法会在同一个线程中调用
    //如果不配合的话findById是一个线程,@HystrixCommand是一个隔离的线程相当于两个线程
    //正常情况下不需要配置,等抛异常了在配置 @GetMapping("/movie/{id}") @HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")) public User findById(@PathVariable Long id) { return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class); } public User findByIdFallback(Long id) { User user = new User(); user.setId(0L); return user; } }

      实体类:

    package com.itmuch.cloud.entity;
    
    import java.math.BigDecimal;
    
    public class User {
      private Long id;
    
      private String username;
    
      private String name;
    
      private Short age;
    
      private BigDecimal balance;
    
      public Long getId() {
        return this.id;
      }
    
      public void setId(Long id) {
        this.id = id;
      }
    
      public String getUsername() {
        return this.username;
      }
    
      public void setUsername(String username) {
        this.username = username;
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    
      public Short getAge() {
        return this.age;
      }
    
      public void setAge(Short age) {
        this.age = age;
      }
    
      public BigDecimal getBalance() {
        return this.balance;
      }
    
      public void setBalance(BigDecimal balance) {
        this.balance = balance;
      }
    
    }

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

    需导入如下jar:

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

     访问: http://localhost:8010/movie/1

        结果如下:{"id":1,"username":"user1","name":"张三","age":20,"balance":100.00}

        访问: http://localhost:8010/health

        得到如下结果:

    Hystrix的状态是UP,也就是一切正常,此时断路器是关闭的。

     停止user微服务,
    访问:http://localhost:8010/user/1,
    可获得如下结果:{"id":0,"username":null,"name":null,"age":null,"balance":null}
    访问: http://localhost:8010/health

    我们发现,尽管执行了回退逻辑,返回了默认用户,但此时Hystrix的状态依然是UP,这是因为我们的失败率还没达到阈值(默认是5秒内20次失败),

    这里再次强调,执行回退逻辑并不代表断路器已经打开。请求失败、超时、被拒绝以及断路器打开时都会执行回退逻辑。

     
    访问: http://localhost:8010/health
     
    可以看到,Hystrix的状态是CiRCUIT_OPEN,说明断路器已经打开,不会再去请求微服务了。

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

    Hystrix的隔离策略有两种:分别是线程隔离和信号量隔离。

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

    Hystrix中默认并且推荐使用线程隔离(THREAD),因为这种方式有一个除网络超时以外的额外保护层。

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

           信号量隔离一般仅适用于非网络调用的隔离。

     7.2.5. Feign使用Hystrix

      7.2.5.1. 为Feign添加回退

      7.2.5.2. 通过Fallback Factory检查回退原因

      7.2.5.3. 为Feign禁用Hystrix

    7.3. Hystrix的监控

    7.4. 使用Hystrix Dashboard可视化监控数据

    7.5. 使用Turbine聚合监控数据

     7.5.1. Turbine简介

     7.5.2. 使用Turbine监控多个微服务

     7.5.3. 使用消息中间件收集数据

     7.5.3.1. 安装RabbitMQ

     7.5.3.2. 改造微服务

     7.5.3.3. 改造Turbine

  • 相关阅读:
    将程序集从GAC中导出
    客户端访问WebService复杂参数类型的使用和参数的序列化与反序列化
    (译Workflow in the 2007 Microsoft Office System)介绍(部分)
    处理PageRequestManager事件
    测试 IPersonalizable接口
    几篇很好的文章
    交换两个数,不引入第三个变量
    Dijkstra算法
    给定一个整数,求它的二进制表示中有多少个1。
    自律
  • 原文地址:https://www.cnblogs.com/caoyingjielxq/p/9723581.html
Copyright © 2011-2022 走看看