Spring Cloud Hystrix断路器的原理和案例实战

版权声明:本博客都是作者10多年工作总结 https://blog.csdn.net/Peter_Changyb/article/details/82464275

原理:hystrix通过服务隔离、熔断(也可以称为断路)、降级等手段控制依赖服务的延迟与失败。

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

正常情况下的服务:一服务出现异常,拖垮整个服务链路,消耗整个线程队列,造成服务不可用,资源耗尽:

  1. 硬件故障:硬件损坏造成的服务器主机宕机, 网络硬件故障造成的服务提供者的不可访问
  2. 程序Bug
  3. 缓存击穿:缓存击穿一般发生在缓存应用重启, 所有缓存被清空时,以及短时间内大量缓存失效时. 大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用
  4. 用户大量请求:在秒杀和大促开始前,如果准备不充分,用户发起大量请求也会造成服务提供者的不可用
  5. 用户重试:在服务提供者不可用后, 用户由于忍受不了界面上长时间的等待,而不断刷新页面甚至提交表单
  6. 代码逻辑重试: 服务调用端的会存在大量服务异常后的重试逻辑
  7. 同步等待造成的资源耗尽:当服务调用者使用同步调用 时, 会产生大量的等待线程占用系统资源. 一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态, 于是服务雪崩效应产生了。

     Hystrix 是一个帮助解决分布式系统交互时超时处理和容错的类库, 它同样拥有保护系统的能力。Netflix的众多开源项目之一。

   1. 隔离:原理:用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被阻塞,至少可以看到一个执行结果(例如返回友好的提示信息),而不是无休止的等待或者看到系统崩溃。

         Hystrix隔离方式采用线程/信号的方式,通过隔离限制依赖的并发量和阻塞扩散

          1 ) 线程隔离

          Hystrix在用户请求和服务之间加入了线程池。 Hystrix为每个依赖调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。线程数是可以被设定的。

         2)信号隔离:

       信号隔离也可以用于限制并发访问,防止阻塞扩散, 与线程隔离最大不同在于执行依赖代码的线程依然是请求线程(该线程需要通过信号申请, 如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离,降低开销。信号量的大小可以动态调整, 线程池大小不可以。

   2. 熔断:

如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

熔断器:Circuit Breaker

    熔断器是位于线程池之前的组件。用户请求某一服务之后,Hystrix会先经过熔断器,此时如果熔断器的状态是打开(跳起),则说明已经熔断,这时将直接进行降级处理,不会继续将请求发到线程池。熔断器相当于在线程池之前的一层屏障。每个熔断器默认维护10个bucket ,每秒创建一个bucket ,每个blucket记录成功,失败,超时,拒绝的次数。当有新的bucket被创建时,最旧的bucket会被抛弃。

 流程说明:

1:每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中。

2:执行execute()/queue做同步或异步调用。

3:判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进行降级策略,如果关闭进入步骤。

4:判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8,否则继续后续步骤。

5:调用HystrixCommand的run方法。运行依赖逻辑

5a:依赖逻辑调用超时,进入步骤8。

6:判断逻辑是否调用成功

6a:返回成功调用结果

6b:调用出错,进入步骤8。

7:计算熔断器状态,所有的运行状态(成功, 失败, 拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态。

8:getFallback()降级逻辑。

以下四种情况将触发getFallback调用:

(1):run()方法抛出非HystrixBadRequestException异常

(2):run()方法调用超时

(3):熔断器开启拦截调用

(4):线程池/队列/信号量是否跑满

8a:没有实现getFallback的Command将直接抛出异常

8b:fallback降级逻辑调用成功直接返回

8c:降级逻辑调用失败抛出异常

9:返回执行成功结果

同步执行:即一旦开始执行该命令,当前线程就得阻塞着直到该命令返回结果,然后才能继续执行下面的逻辑

异步执行:命令开始执行会返回一个Future<T>的对象,不阻塞后面的逻辑,开发者自己根据需要去获取结果。

响应式执行:命令开始执行会返回一个Observable<T> 对象,开发者可以给给Obeservable对象注册上Observer或者Action1对象,响应式地处理命令执行过程中的不同阶段。

实战如下,

  • 依赖引入

  <!--hystrix-->

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-hystrix</artifactId>

        </dependency>

        <!--hystrix-dashboard 监控-->

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>

        </dependency>

  • 主程序如下:

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.builder.SpringApplicationBuilder;

import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;

import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**

 * Created by changyaobin on 18/5/1.

 */

@SpringBootApplication

@EnableCircuitBreaker

@EnableHystrixDashboard

public class Application {

    public static void main(String[] args) {

        new SpringApplicationBuilder(Application.class).web(true).run(args);

    }

}

  • 模拟调用接口

import com.lkl.springcloud.hystrix.service.HystrixService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

/**

 * 模拟一个对外的接口

 * Created by changyaobin

 */

@RestController

public class HystrixController {

    @Autowired

    private HystrixService service;

    /**

     * 调用依赖的服务

     */

    @RequestMapping("/call")

    public String callDependencyService(){

        return service.callDependencyService();

    }

}

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

/**

 * 依赖服务HystrixService

Created by changyaobin */

@Service

public class HystrixService {

    @Autowired

    private CallDependencyService dependencyService;

    public String callDependencyService() {

        return dependencyService.mockGetUserInfo();

    }

}

  • 依赖服务CallDependencyService

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

import org.springframework.stereotype.Component;

import java.util.Random;

/**

 * 调用依赖服务,通过hystrix包装调用服务

 * Created by changyaobin

 */

@Component

public class CallDependencyService {

 

    private Random random = new Random();

    /**

     * 模拟获取用户信息(通过网络调用)

     * @return

     */

    @HystrixCommand(fallbackMethod = "fallback")

    public String mockGetUserInfo(){

        int randomInt= random.nextInt(10) ;

        if(randomInt<8){  //模拟调用失败情况

            throw new RuntimeException("call dependency service fail.");

        }else{

            return "UserName:liaokailin;number:"+randomInt;

        }

    }

    public String fallback(){

        return "some exception occur call fallback method.";

    }

}

猜你喜欢

转载自blog.csdn.net/Peter_Changyb/article/details/82464275