介绍
Bulkhead意指船舶中的隔舱板,它将船体分割为多个船舱,在船部分受损时可避免沉船。框架中的Bulkhead通过信号量的方式隔离不同种类的调用,并进行流控,这样可以避免某类调用异常危及系统整体。(注:不同于Hystrix,该框架不提供基于线程池的隔离)
初始化Bulkhead
Bulkhead的配置方式与熔断类似,有对应的BulkheadRegistry 和 BulkheadConfig,自定义配置的可选项有:
- 最大并行度
- 尝试进入饱和态的Bulkhead时,线程的最大阻塞时间
// 创建自定义的Bulkhead配置
BulkheadConfig config = BulkheadConfig.custom() .maxConcurrentCalls(150) .maxWaitTime(100) .build(); // 创建Bulkhead注册中心
BulkheadRegistry registry = BulkheadRegistry.of(config); // 从注册中心获取默认配置的Bulkhead
Bulkhead bulkhead1 = registry.bulkhead("foo"); // 从注册中心获取自定义配置的Bulkhead
Bulkhead bulkhead2 = registry.bulkhead("bar", config);
你也可以不经过注册中心,直接创建Bulkhead:
Bulkhead bulkhead1 = Bulkhead.ofDefaults("foo");
Bulkhead bulkhead2 = Bulkhead.of( "bar", BulkheadConfig.custom() .maxConcurrentCalls(50) .build() );
Bulkhead使用方式
与熔断类似,不再赘述,下面是代码示例:
// 创建Bulkhead实例
Bulkhead bulkhead = Bulkhead.of("testName", config);
// 用Bulkhead包装函数调用
CheckedFunction0<String> decoratedSupplier = Bulkhead.decorateCheckedSupplier(bulkhead, () -> "This can be any method which returns: 'Hello");
// 链接其它函数
Try<String> result = Try.of(decoratedSupplier) .map(value -> value + " world'");
assertThat(result.isSuccess()).isTrue();
assertThat(result.get()).isEqualTo("This can be any method which returns: 'Hello world'");
assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1);
函数链中可以包含被不同Bulkhead或熔断器包装的多个函数:
// 两个Bulkhead
Bulkhead bulkhead = Bulkhead.of("test", config);
Bulkhead anotherBulkhead = Bulkhead.of("testAnother", config);
// 用两个Bulkhead分别包装Supplier 和 Function
CheckedFunction0<String> decoratedSupplier = Bulkhead.decorateCheckedSupplier(bulkhead, () -> "Hello");
CheckedFunction1<String, String> decoratedFunction = Bulkhead.decorateCheckedFunction(anotherBulkhead, (input) -> input + " world");
// 链接函数
Try<String> result = Try.of(decoratedSupplier) .mapTry(decoratedFunction::apply);
assertThat(result.isSuccess()).isTrue();
assertThat(result.get()).isEqualTo("Hello world");
assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1);
assertThat(anotherBulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1);
下面我们模拟饱和态Bulkhead的行为:
// 创建并行度为2的Bulkhead
BulkheadConfig config = BulkheadConfig.custom().maxConcurrentCalls(2).build();
Bulkhead bulkhead = Bulkhead.of("test", config);
// 该方法会进入Bulkhead
bulkhead.isCallPermitted();
bulkhead.isCallPermitted();
// 经过上面两次调用,Bulkhead已饱和
CheckedRunnable checkedRunnable = Bulkhead.decorateCheckedRunnable(bulkhead, () -> {throw new RuntimeException("BAM!");});
Try result = Try.run(checkedRunnable);
assertThat(result.isFailure()).isTrue();
assertThat(result.failed().get()).isInstanceOf(BulkheadFullException.class);
你可以在运行时动态修改Bulkhead配置,但新的最大阻塞时间不会影响当前正在等待的线程。
与熔断类似,Bulkhead也支持RxJava/Reactor.
Bulkhead bulkhead = Bulkhead.ofDefaults("backendName");
Observable.fromCallable(backendService::doSomething) .lift(BulkheadOperator.of(bulkhead));
Bulkhead bulkhead = Bulkhead.ofDefaults("backendName");
Mono.fromCallable(backendService::doSomething) .transform(BulkheadOperator.of(bulkhead));
监听Bulkhead事件
BulkHeadEvent包含允许执行、拒绝执行、执行结束事件,可以用如下方式监听:
bulkhead.getEventPublisher() .onCallPermitted(event -> logger.info(...)) .onCallRejected(event -> logger.info(...)) .onCallFinished(event -> logger.info(...));
Bulkhead状态
Bulkhead.Metrics metrics = bulkhead.getMetrics();
in remainingBulkheadDepth = metrics.getAvailableConcurrentCalls()