zoukankan      html  css  js  c++  java
  • springcloud-Hystrix 容错处理

    • 实现容错的常见三种机制
    • 在项目中如何使用 Hystrix

    我们已实现服务发现和负载均衡,同时,使用 Feign 也实现了良好的远程调用——使得我们的代码是可读和可维护的。理论上,我们现在已经可以构建一个不错的分布式应用了,但微服务之间是通过网络通信的,网络可能出现问题,同时,微服务本身也并非 100% 可用。

    如何提升应用的可用性呢?这是我们必须考虑的问题。

    举个例子:某大型系统中,服务 A 调用服务 B,某个时刻,微服务 B 突然崩溃了,但微服务 A 中依然有大量请求在请求 B,如果没有任何措施,微服务 A 很可能很快就会被拖死——因为在 Java 中,一次请求往往对应着一个线程,如果不做任何措施,那意味着微服务 A 请求 B 的线程要等 Feign Client/RestTemplate 超时才会释放(这个时间一般非常长,长达几十秒),于是就会有大量的线程被阻塞,而线程又对应着计算资源(CPU/内存),于是乎,大量的资源被浪费,并且越积越多,最终服务器终于没有资源给微服务 A 浪费了,微服务 A 也挂了。如果在线的服务更多的话,比如几十个或者上百个,那么后果是不可想象的

    容错手段

    • 超时机制

    即在服务中配置一下超时时间,例如超时时间的阈值为 1 秒——请求在 1 秒内必须返回,否则到点就把线程掐死,释放资源!这样,请求一旦超时,就会释放资源。由于释放资源速度较快,应用就不会那么容易被拖死。

    • 舱壁模式

    一般来说,现代的轮船都会分很多舱室,舱室之间用钢板焊死,彼此隔离。这样即使有某个/某些船舱进水,也不会影响其他舱室,浮力够,船不会沉。

    软件世界里的仓壁模式可以这样理解:M 类使用线程池 1,N 类使用线程池 2,彼此的线程池不同,并且为每个类分配的线程池较小,例如 coreSize=10。 就好比人们常说的一句话 “不把鸡蛋放在一个篮子里”。你有你的线程池,我有我的线程池,你的线程池满了和我没关系,你挂了也和我没关系。

    • 断路器

    现实世界的断路器大家肯定都很了解,每个人家里都会有断路器。断路器实时监控电路的情况,如果发现电路电流异常,就会跳闸,从而防止电路被烧毁。

    软件世界的断路器可以这样理解:实时监测应用,如果发现在一定时间内失败次数或者失败率达到一定阈值,就“跳闸”,断路器打开——此时,请求直接返回,而不去调用原本调用的逻辑。

    如何使用 Hystrix

    Hystrix 是由 Netflix 开源的一个延迟和容错库,用于隔离访问远程系统、服务和第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix 主要通过以下几点实现延迟和容错:

    • 包裹请求

    使用 HystrixCommand(或 HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用到了设计模式中的“命令模式”。

    • 跳闸机制

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

    • 资源隔离

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

    • 监控

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

    • 回退机制

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

    • 自我修复

    断路器打开一段时间后,会自动进入“半开”状态。断路器打开、关闭、半开的逻辑转换,前面我们已经详细探讨过了,不再赘述。

     
     
     当电影服务调用用户服务接口出现问题的时候,触发熔断,返回默认的值。
     在 microservice-consumer-movie-ribbon 的 pom.xml 中引入 Hystrix 依赖
     
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    

      

     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>
    
        <groupId>com.lzj1234</groupId>
        <artifactId>microservice-consumer-movie-ribbon</artifactId>
        <version>1.0-SNAPSHOT</version>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.7.RELEASE</version>
            <relativePath/>
        </parent>
    
        <properties>
            <java.version>1.8</java.version>
    <!--        <maven.compiler.source>8</maven.compiler.source>-->
    <!--        <maven.compiler.target>8</maven.compiler.target>-->
        </properties>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <!--            <version>2.4.2</version>-->
            </dependency>
    
            <!-- 引入H2数据库,一种内嵌的数据库,语法类似MySQL -->
            <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <version>1.4.200</version>
                <!--            <scope>test</scope>-->
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.18</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <!--            <version>2.4.2</version>-->
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-clean-plugin -->
            <dependency>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>2.5</version>
            </dependency>
            <!-- 新增依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        </dependencies>
    
        <!-- 引入spring cloud的依赖,不能少,主要用来管理Spring Cloud生态各组件的版本 -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Finchley.SR2</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.5.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

      加注解:在启动类App.java上添加 @EnableCircuitBreaker 注解 

    @LoadBalanced

    app.java

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

      

    MovieController

    package com.lzj1234;
    
    
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import com.netflix.ribbon.proxy.annotation.Hystrix;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.math.BigDecimal;
    
    @RequestMapping("/movies")
    @RestController
    public class MovieController {
    
        @Autowired
        RestTemplate restTemplate;
    
    
        @HystrixCommand(fallbackMethod = "findByIdFallback")
        @GetMapping("/users/{id}")
        public User findById(@PathVariable Long id){
            // 这里用到了RestTemplate的占位符能力  microservice-provider-user
            User user=this.restTemplate.getForObject("http://microservice-provider-user/users/{id}",User.class,id);
            return user;
        }
    
        public  User findByIdFallback(Long id ){
            return new User(id,"default username","default name",0,new BigDecimal(1));
        }
    
    
    }
    

      以上代码的作用:@HystrixCommand(fallbackMethod = "findByIdFallback") 使用了一个 fallbackMethod 为 /users/{id} 接口添加了熔断机制,在该接口请求过程中出错时会调用 findByIdFallback 方法返回一个默认的用户值

    菜鸟的自白
  • 相关阅读:
    6. ModelDriven拦截器、Preparable 拦截器
    5. 标签和主题
    前后端开发联调遇到的问题以及排查
    手写HashTable
    Java项目常用注解总结
    快速排序就这么简单
    交替打印出奇数和偶数
    Java的SpringMVC执行流程
    Java中Comparable与Comparator的区别
    阻塞非阻塞,同步和异步的概念
  • 原文地址:https://www.cnblogs.com/lzjloveit/p/14418841.html
Copyright © 2011-2022 走看看