SpringCloud.Hoxton 版本, 全新熔斷器CircuitBreaker
- SpringCloud.Hoxton 版本, 全新熔斷器CircuitBreaker
-
- 前置說明
- 如何使用
- 自動裝配
- 總結
SpringCloud.Hoxton 版本, 全新熔斷器CircuitBreaker
前置說明
上文講到了Honxton 版本的新的負載均衡器, 這次準備講一下新的熔斷器. 其實也不是新的熔斷器, 是一個新的統一接口. 用過hystrix 的應該知道, 其實它整合到springcloud的時候, 其用法包括注解方式還是程式設計方式都是hystrix自帶的. 這樣的用法有個不好的地方就是, 如果我要切換新的熔斷器(hystrix官方不在更新,而是推薦我們使用Resilience4J, 雖然本身hystrix已經比較成熟了,不替換也不會有太多問題). 這就有點蛋疼了, 雖然網關那裡隻要改下依賴和配置就行了, 但是許多微服務中無論使用了注解的方式還是程式設計的方式,我都要一一替換.
是以在這個版本, springcloud提供了熔斷的統一接口(暫時好像沒有注解的方式, 但是我們可以自己用aop實作)
CircuitBreakerFactory
,
ReactiveCircuitBreakerFactory
. 這兩個接口分别用于非響應式和響應式程式設計.
主要的依賴, 也可以直接下載下傳我之前的 sample-springcloud 項目, 裡面有 h版本的分支demo, 連結放到評論區
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
如何使用
首先介紹一下非響應式的統一熔斷接口
CircuitBreakerFactory
// 基本用法
@Autowired
private CircuitBreakerFactory cbFactory;
@Autowired
private RestTemplate rest;
public String slow() {
return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
}
首先很明顯最開始是一個工廠方法, 是以會調用create方法, 這個接口的參數是一個個
id
, 這個id是用來擷取對應的配置的.具體看下面,分别是
hystrix
和
resilience4j
的
create
方法
// org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerFactory#create
public HystrixCircuitBreaker create(String id) {
Assert.hasText(id, "A CircuitBreaker must have an id.");
HystrixCommand.Setter setter = getConfigurations().computeIfAbsent(id,
defaultConfiguration);
return new HystrixCircuitBreaker(setter);
}
// org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory#create
public Resilience4JCircuitBreaker create(String id) {
Assert.hasText(id, "A CircuitBreaker must have an id.");
Resilience4JConfigBuilder.Resilience4JCircuitBreakerConfiguration config = getConfigurations()
.computeIfAbsent(id, defaultConfiguration);
return new Resilience4JCircuitBreaker(id, config.getCircuitBreakerConfig(),
config.getTimeLimiterConfig(), circuitBreakerRegistry, executorService,
Optional.ofNullable(circuitBreakerCustomizers.get(id)));
}
可以看到基本都是擷取相關的配置, 沒有就取預設的配置.
接下來run方法, 第一個參數就是原邏輯執行, 第二個參數是降級邏輯.
// org.springframework.cloud.netflix.hystrix.HystrixCircuitBreaker#run
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
// 這裡的用法就是程式設計式的用法,沒啥差別
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(); //這裡就是hystrix的真正執行了, 底層是rxjava實作的
}
// org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreaker#run
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
TimeLimiter timeLimiter = TimeLimiter.of(timeLimiterConfig);
Supplier<Future<T>> futureSupplier = () -> executorService.submit(toRun::get); // 将原邏輯丢到線程池, 獲得一個future
Callable restrictedCall = TimeLimiter.decorateFutureSupplier(timeLimiter,
futureSupplier); // 裝飾上 逾時邏輯, 其實很簡單哈 ,就是通過Future的逾時擷取就可以了
io.github.resilience4j.circuitbreaker.CircuitBreaker defaultCircuitBreaker = registry
.circuitBreaker(id, circuitBreakerConfig);
circuitBreakerCustomizer
.ifPresent(customizer -> customizer.customize(defaultCircuitBreaker));
Callable<T> callable = io.github.resilience4j.circuitbreaker.CircuitBreaker
.decorateCallable(defaultCircuitBreaker, restrictedCall);
return Try.of(callable::call).recover(fallback).get(); // 執行邏輯和降級處理
}
以上就是執行的過程, 可以看出
resilience4j
是要輕量很多, 而
hystrix
的源碼看下去能看上一整天 /(ㄒoㄒ)/~~
接下來是響應式接口的使用
@Autowired
private WebClient webClient; //對應restTemplate響應式版本
@Autowired
private ReactiveCircuitBreakerFactory rcbFactory; //響應式熔斷工廠
public Mono<String> slow1() {
return webClient.get().uri("/slow").retrieve() // 拿到DefaultResponseSpec
.bodyToMono(String.class) // 轉換Mono<ClientResponse> -> Mono<String>
.transform( // 轉換 Mono<String> 增加 熔斷邏輯
it -> rcbFactory.create("slow").run(it, throwable -> {
return Mono.just("fallback");
}));
}
具體還是看下 run 的實作
//org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreaker#run(reactor.core.publisher.Mono<T>, java.util.function.Function<java.lang.Throwable,reactor.core.publisher.Mono<T>>)
public <T> Mono<T> run(Mono<T> toRun, Function<Throwable, Mono<T>> fallback) {
HystrixObservableCommand<T> command = createCommand(toRun, fallback);
return Mono.create(s -> {
// 這裡也能看到 hystrix 底層就是響應式的, 隻不過可以通過阻塞方法适配成非響應式的調用, 不過因為spring用reactor 而 hystrix 用的rxjava, 是以需要通過标準接口(reactive stream規範)進行轉換一下
Subscription sub = command.toObservable().subscribe(s::success, s::error,
s::success);
s.onCancel(sub::unsubscribe);
});
}
// org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreaker#run(reactor.core.publisher.Mono<T>, java.util.function.Function<java.lang.Throwable,reactor.core.publisher.Mono<T>>)
public <T> Mono<T> run(Mono<T> toRun, Function<Throwable, Mono<T>> fallback) {
io.github.resilience4j.circuitbreaker.CircuitBreaker defaultCircuitBreaker = registry
.circuitBreaker(id, config.getCircuitBreakerConfig());
circuitBreakerCustomizer
.ifPresent(customizer -> customizer.customize(defaultCircuitBreaker));
Mono<T> toReturn = toRun
.transform(CircuitBreakerOperator.of(defaultCircuitBreaker))
.timeout(config.getTimeLimiterConfig().getTimeoutDuration()) // reactor 提供的逾時設定
.doOnError(TimeoutException.class,
t -> defaultCircuitBreaker.onError(config.getTimeLimiterConfig()
.getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS,
t));
if (fallback != null) {
toReturn = toReturn.onErrorResume(fallback);
}
return toReturn;
}
可以看出來resilience4j的響應式還是通過spring去适配的, 本身沒有提供響應式的api.
自動裝配
先看下 hystrix 的自動裝配
@Configuration(proxyBeanMethods = false) // sb新特性, 減少運作時的類生成
@ConditionalOnClass({ Hystrix.class }) //必須引入hystrix依賴
@ConditionalOnProperty(name = "spring.cloud.circuitbreaker.hystrix.enabled",
matchIfMissing = true) // 配置開啟, 預設就是開啟的
public class HystrixCircuitBreakerAutoConfiguration {
// 對應的工廠類
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public CircuitBreakerFactory hystrixCircuitBreakerFactory() {
return new HystrixCircuitBreakerFactory();
}
// 響應式的工廠類
@Bean
@ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class)
@ConditionalOnClass(
name = { "reactor.core.publisher.Mono", "reactor.core.publisher.Flux" })
public ReactiveHystrixCircuitBreakerFactory reactiveHystrixCircuitBreakerFactory() {
return new ReactiveHystrixCircuitBreakerFactory();
}
// 自定義定制
@Configuration(proxyBeanMethods = false)
protected static class HystrixCircuitBreakerCustomizerConfiguration {
@Autowired(required = false)
private List<Customizer<HystrixCircuitBreakerFactory>> customizers = new ArrayList<>();
@Autowired(required = false)
private HystrixCircuitBreakerFactory factory;
@PostConstruct
public void init() {
customizers.forEach(customizer -> customizer.customize(factory));
}
}
// 自定義定制
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(
name = { "reactor.core.publisher.Mono", "reactor.core.publisher.Flux" })
protected static class ReactiveHystrixCircuitBreakerCustomizerConfiguration {
@Autowired(required = false)
private List<Customizer<ReactiveHystrixCircuitBreakerFactory>> customizers = new ArrayList<>();
@Autowired(required = false)
private ReactiveHystrixCircuitBreakerFactory factory;
@PostConstruct
public void init() {
customizers.forEach(customizer -> customizer.customize(factory));
}
}
}
resilience4j的自動配置也是類似
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.circuitbreaker.resilience4j.enabled",
matchIfMissing = true) //預設開啟
public class Resilience4JAutoConfiguration {
// 工廠類
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public Resilience4JCircuitBreakerFactory resilience4jCircuitBreakerFactory() {
return new Resilience4JCircuitBreakerFactory();
}
// 自定義配置,定制的
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass({
"io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics",
"io.micrometer.core.instrument.MeterRegistry" })
public static class Resilience4JCustomizerConfiguration {
@Autowired(required = false)
private List<Customizer<Resilience4JCircuitBreakerFactory>> customizers = new ArrayList<>();
@Autowired(required = false)
private Resilience4JCircuitBreakerFactory factory;
@PostConstruct
public void init() {
customizers.forEach(customizer -> customizer.customize(factory));
}
}
// 用于監控的
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean({ MeterRegistry.class }) //springboot actuator這個依賴要有
@ConditionalOnClass(name = {
"io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics" })
public static class MicrometerResilience4JCustomizerConfiguration {
@Autowired(required = false)
private List<Customizer<Resilience4JCircuitBreakerFactory>> customizers = new ArrayList<>();
@Autowired(required = false)
private Resilience4JCircuitBreakerFactory factory;
@Autowired
private MeterRegistry meterRegistry;
@PostConstruct
public void init() {
customizers.forEach(customizer -> customizer.customize(factory));
if (factory != null) {
TaggedCircuitBreakerMetrics
.ofCircuitBreakerRegistry(factory.getCircuitBreakerRegistry())
.bindTo(meterRegistry);
}
}
}
}
基本大同小異
總結
在H 版本以後, 熔斷提供了新的統一接口, 這樣就可以快速的替換底層的實作.
關于響應式: 因為spring開始推廣響應式程式設計(reactor), 是以在各個地方包塊webflux, webclient等都能見到響應式的使用. 響應式可以大大降低并發程式設計的困難, 但是它繁多的api和複雜的底層實作也會讓學習的曲線很陡. 當然響應式能不能提升性能這個就仁者見仁了.
關于hystrix vs resilience4j: 兩者都是很優秀的熔斷架構,簡單來說功能上兩者都有熔斷的功能, 逾時的功能, 降級的功能; 架構上resilience4j會稍微輕量很多; 性能上, resilience4j因為它的輕量級的實作, 是以性能會稍好(即使hystrix 底層用的rxjava, 但是我的了解是響應式并不能提升性能); 隔離性上我覺得hystrix基于線程池的隔離會更加好點.
分析到此結束了, 歡迎大家能互相交流