Spring Cloud 中断路器 Circuit Breaker的应用

环境:Springboot2.3.12.RELEASE +
cloud-netflix-hystrix2.2.10.RELEASE


简介

SpringCloud Circuit breaker(断路器)提供了跨不同断路器实现的抽象。它提供了在应用程序中使用的一致API,允许开发人员选择最适合应用程序需要的断路器实现。

支持的断路器类型:

  • Netfix Hystrix
  • Resilience4J
  • Sentinel
  • Spring Retry

核心概念

要在代码中创建断路器(circuit breaker),可以使用断路器工厂API。当您在类路径中包含Spring Cloud Circuit Breaker starter时,将自动创建一个实现此API的bean。下面给出了使用此API的一个非常简单的示例:

@Service
public static class DemoService {
  private RestTemplate rest;
  private CircuitBreakerFactory cbFactory;

  public DemoService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
    this.rest = rest;
    this.cbFactory = cbFactory;
  }

  public String slow() {
    // 通过默认的CircuitBreakerFactory工厂创建一个指定id(名称)的断路器
    // run方法是实际执行你的业务方法,第二个参数throwable 是当发生异常或者是执行超时
    // 执行的回退(降级)处理
    return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
  }
}

项目配置

通过引入下面不同依赖来确定使用具体的那个断路器

  • Hystrix - org.springframework.cloud:spring-cloud-starter-netflix-hystrix
  • Resilience4J - org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j
  • Reactive Resilience4J - org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j
  • Spring Retry - org.springframework.cloud:spring-cloud-starter-circuitbreaker-spring-retry
  • Sentinal - org.springframework.cloud:spring-cloud-starter-circuitbreaker-sentinal

以上5种断路器是不同的实现方式,根据需要引入即可。

示例

这里以Hystrix为例来使用

引入依赖

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

定义具有熔断功能的服务

@Service
public class DemoService {

  private RestTemplate rest;
  // 注入系统默认的实现
  private CircuitBreakerFactory cbFactory;

  public DemoService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
    this.rest = rest;
    this.cbFactory = cbFactory;
  }

  public String slow() {
    // 使用系统默认的实现创建断路器进行业务的处理
    return cbFactory.create("slow").run(() -> rest.getForObject("http://localhost:8080/demos/slow", String.class), throwable -> "fallback");
  }

  public String slow2() {
    // 使用自定义的断路器工厂进行业务的处理
    return cbf().create("demo-slow").run(() -> rest.getForObject("http://localhost:8080/demos/slow", String.class), throwable -> "fallback");
  }

  // 可以将这个定义为Bean来覆盖系统默认的实现,在系统默认的实现上有条件限定
  private CircuitBreakerFactory<HystrixCommand.Setter, HystrixCircuitBreakerFactory.HystrixConfigBuilder> cbf() {
    HystrixCircuitBreakerFactory cbf = new HystrixCircuitBreakerFactory() ;
    // 配置线程池
    HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter() ;
    threadPoolProperties.withCoreSize(5)
      .withKeepAliveTimeMinutes(5)
      .withMaxQueueSize(Integer.MAX_VALUE)
      .withQueueSizeRejectionThreshold(1000) ;
    // 配置默认的执行行为属性
    HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter() ;
    commandProperties.withCircuitBreakerEnabled(true)
    // 当请求超过了3s那么断路器就会工作进行回退(降级处理),执行上面run方法中的第二个参数
      .withExecutionTimeoutInMilliseconds(3000)
      .withRequestCacheEnabled(true)
      // 隔离策略有两种THREAD,SEMAPHORE
      // THREAD: 避免线程被阻塞
      // SEMAPHORE: 适合高并发限流处理;因为线程池的方式一般不会创建过多的线程
      // 线程是有限的,在高并发情况下是没法满足响应处理的。
      .withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD);
		
    // 将其加入到集合中,为不同的服务创建不同的配置
    cbf.configure(builder -> {
      builder.commandProperties(commandProperties).groupName("demo") ;
    }, "demo-slow");
	    
    // 当默认的id不存在时使用这默认的配置
    cbf.configureDefault(id -> {
      HystrixCommand.Setter setter = HystrixCommand.Setter
        .withGroupKey(HystrixCommandGroupKey.Factory.asKey("demo")) // 服务分组,大的模块
        .andCommandKey(HystrixCommandKey.Factory.asKey("demo-slow")) // 服务标识(具体服务分组中的某一个子的服务),子模块
        .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("demo-pools")) // 线程池名称
        .andThreadPoolPropertiesDefaults(threadPoolProperties) // 线程池相关配置
        .andCommandPropertiesDefaults(commandProperties) ; // 执行时相关属性配置 
      return setter ;
    });
    return cbf ;
  }

}

Controller接口

@RestController
@RequestMapping("/demos")
public class DemoController {
	
  @Resource
  private DemoService demoService ;
	
  @GetMapping("/index")
  public Object index() {
    return demoService.slow2() ;
  }
	
  @GetMapping("/slow")
  public Object slow() {
    try {
      TimeUnit.SECONDS.sleep(5) ;
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return "slow" ;
  }
	
}

原理

CircuitBreakerFactory#create方法创建了CircuitBreaker实例

根据当前的CLASSPATH我们使用的是Hystrix,那么这里使用的工厂就是:


HystrixCircuitBreakerFactory类

public class HystrixCircuitBreakerFactory extends CircuitBreakerFactory<HystrixCommand.Setter, HystrixCircuitBreakerFactory.HystrixConfigBuilder> {

泛型参数:Setter就是用来配置Hystrix相关配置信息的(这里主要用来CommandKey与Setter进行绑定),HystrixConfigBuilder用来构建 HystrixCommand.Setter对象。

当执行
HystrixCircuitBreakerFactory#configure方法时:

public abstract class AbstractCircuitBreakerFactory<CONF, CONFB extends ConfigBuilder<CONF>> {
  private final ConcurrentHashMap<String, CONF> configurations = new ConcurrentHashMap<>();
  public void configure(Consumer<CONFB> consumer, String... ids) {
    for (String id : ids) {
      // 构建一个Builder对象
      CONFB builder = configBuilder(id);
      // 这里通过builder(HystrixConfigBuilder)对象来应用Consumer中编写的配置信息
      consumer.accept(builder);
      // 构建HystrixCommand.Setter 对象
      CONF conf = builder.build();
      // 最后将通过id 与 Setter对象绑定key=value存入Map集合中
      getConfigurations().put(id, conf);
    }
  }
  // 该方法在子类HystrixCircuitBreakerFactory中实现
  protected abstract CONFB configBuilder(String id);
}

断路器具体的子类实现
HystrixCircuitBreakerFactory

// 子类继承的父类中的泛型:第一个泛型参数:需要构建什么样的一个配置,第二个泛型参数:通过谁来构建第一个泛型参数配置
public class HystrixCircuitBreakerFactory extends CircuitBreakerFactory<HystrixCommand.Setter, HystrixCircuitBreakerFactory.HystrixConfigBuilder> {
  public HystrixConfigBuilder configBuilder(String id) {
    return new HystrixConfigBuilder(id);
  }
  public static class HystrixConfigBuilder extends AbstractHystrixConfigBuilder<HystrixCommand.Setter> {
    public HystrixConfigBuilder(String id) {
      super(id);
    }
    // 从这里也看出来最终Builder就是用来构建Setter对象用
    @Override
    public HystrixCommand.Setter build() {
      return HystrixCommand.Setter.withGroupKey(getGroupKey())
        .andCommandKey(getCommandKey())
        .andCommandPropertiesDefaults(getCommandPropertiesSetter());
    }
  }
}

断路器工厂有了,接下来就是通过工厂创建具体的断路器对象了

通过上面的代码执行cbf().create("demo-slow")方法时执行了什么?

public class HystrixCircuitBreakerFactory extends CircuitBreakerFactory<HystrixCommand.Setter, HystrixCircuitBreakerFactory.HystrixConfigBuilder> {
  private Function<String, HystrixCommand.Setter> defaultConfiguration = id -> HystrixCommand.Setter
    .withGroupKey(HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName()))
    .andCommandKey(HystrixCommandKey.Factory.asKey(id));
  public HystrixCircuitBreaker create(String id) {
    // 通过上面分析最终所有的Hystrix的Setter会与id绑定存入一个Map中
    // 这里computeIfAbsent方法先从集合中通过id获取,如果获取不到则将第二个参数存入集合中返回
    HystrixCommand.Setter setter = getConfigurations().computeIfAbsent(id, defaultConfiguration);
    return new HystrixCircuitBreaker(setter);
  }
}

上面创建的是HystrixCircuitBreaker断路器,当执行run方法时:

public class HystrixCircuitBreaker implements CircuitBreaker {
  private HystrixCommand.Setter setter;
  public HystrixCircuitBreaker(HystrixCommand.Setter setter) {
    this.setter = setter;
  }
  @Override
  public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
    // 最终执行的就是Hystrix的核心 HystrixCommand对象
    HystrixCommand<T> command = new HystrixCommand<T>(setter) {
      @Override
      protected T run() throws Exception {
        return toRun.get();
      }
      @Override
      protected T getFallback() {
        return fallback.apply(getExecutionException());
      }
    };
    return command.execute();
  }
}

完毕!!!

关注+转发

  图片

图片

图片

图片

图片

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值