下面列出了org.springframework.boot.actuate.endpoint.annotation.ReadOperation#io.github.resilience4j.circuitbreaker.CircuitBreaker 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Test
public void shouldNotBufferEvents() {
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
CircularEventConsumer<CircuitBreakerEvent> ringBuffer = new CircularEventConsumer<>(2);
assertThat(ringBuffer.getBufferedEvents()).isEmpty();
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException("Bla"));
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException("Bla"));
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException("Bla"));
//Subscription is too late
circuitBreaker.getEventPublisher().onEvent(ringBuffer);
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(3);
assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(3);
//Because Subscription was too late
assertThat(ringBuffer.getBufferedEvents()).hasSize(0);
}
@Test
public void decorateUnsuccessfulCall() throws Exception {
stubFor(get(urlPathEqualTo("/greeting"))
.willReturn(aResponse()
.withStatus(500)
.withHeader("Content-Type", "text/plain")));
final Response<String> response = service.greeting().execute();
assertThat(response.code())
.describedAs("Response code")
.isEqualTo(500);
final CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1);
}
@Before
public void init() {
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(1))
.cancelRunningFuture(true)
.build();
timeLimiter = TimeLimiter.of(timeLimiterConfig);
CustomizableThreadFactory factory = new CustomizableThreadFactory("timeLimiter-");
factory.setDaemon(true);
executorService = Executors.newCachedThreadPool(factory);
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig
.custom()
.enableAutomaticTransitionFromOpenToHalfOpen()
.failureRateThreshold(50)
.ringBufferSizeInClosedState(10)
.ringBufferSizeInHalfOpenState(2)
.build();
circuitBreaker = CircuitBreaker.of("backendName", circuitBreakerConfig);
}
@Test
public void shouldCreateCircuitBreakerRegistryWithRegistryStore() {
RegistryEventConsumer<CircuitBreaker> registryEventConsumer = getNoOpsRegistryEventConsumer();
List<RegistryEventConsumer<CircuitBreaker>> registryEventConsumers = new ArrayList<>();
registryEventConsumers.add(registryEventConsumer);
Map<String, CircuitBreakerConfig> configs = new HashMap<>();
final CircuitBreakerConfig defaultConfig = CircuitBreakerConfig.ofDefaults();
configs.put("default", defaultConfig);
final InMemoryCircuitBreakerRegistry inMemoryCircuitBreakerRegistry =
new InMemoryCircuitBreakerRegistry(configs, registryEventConsumers,
io.vavr.collection.HashMap.of("Tag1", "Tag1Value"), new InMemoryRegistryStore());
assertThat(inMemoryCircuitBreakerRegistry).isNotNull();
assertThat(inMemoryCircuitBreakerRegistry.getDefaultConfig()).isEqualTo(defaultConfig);
assertThat(inMemoryCircuitBreakerRegistry.getConfiguration("testNotFound")).isEmpty();
inMemoryCircuitBreakerRegistry.addConfiguration("testConfig", defaultConfig);
assertThat(inMemoryCircuitBreakerRegistry.getConfiguration("testConfig")).isNotNull();
}
@Override
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())
// Since we are using the Mono timeout we need to tell the circuit breaker
// about the error
.doOnError(TimeoutException.class,
t -> defaultCircuitBreaker.onError(config.getTimeLimiterConfig()
.getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS,
t));
if (fallback != null) {
toReturn = toReturn.onErrorResume(fallback);
}
return toReturn;
}
@Test
public void testDecorateCheckedSupplier() throws IOException {
given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
CheckedFunction0<String> decoratedSupplier = Decorators
.ofCheckedSupplier(() -> helloWorldService.returnHelloWorldWithException())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = Try.of(decoratedSupplier).get();
assertThat(result).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorldWithException();
}
@Test
public void testDecorateSupplierWithFallback() {
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
circuitBreaker.transitionToOpenState();
Supplier<String> decoratedSupplier = Decorators
.ofSupplier(() -> helloWorldService.returnHelloWorld())
.withCircuitBreaker(circuitBreaker)
.withFallback(asList(IOException.class, CallNotPermittedException.class), (e) -> "Fallback")
.decorate();
String result = decoratedSupplier.get();
assertThat(result).isEqualTo("Fallback");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfNotPermittedCalls()).isEqualTo(1);
then(helloWorldService).should(never()).returnHelloWorld();
}
@Test
public void testWithCircuitBreakerMetricsPublisher() throws Exception {
CircuitBreakerConfig config =
CircuitBreakerConfig.custom()
.waitDurationInOpenState(Duration.ofSeconds(1))
.failureRateThreshold(50)
.permittedNumberOfCallsInHalfOpenState(3)
.slidingWindowSize(10)
.build();
MetricRegistry metricRegistry = new MetricRegistry();
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry
.of(config, new CircuitBreakerMetricsPublisher(metricRegistry));
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("test", config);
circuitBreakerMetricsUsesFirstStateObjectInstance(circuitBreaker, metricRegistry);
}
@Test
public void testWithCircuitBreaker() throws Throwable {
final CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("test");
final CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
final FeignDecorators testSubject = FeignDecorators.builder()
.withCircuitBreaker(circuitBreaker).build();
final Object result = testSubject.decorate(args -> args[0], null, null, null)
.apply(new Object[]{"test01"});
assertThat(result)
.describedAs("Returned result is correct")
.isEqualTo("test01");
assertThat(metrics.getNumberOfSuccessfulCalls())
.describedAs("Successful Calls")
.isEqualTo(1);
}
@Test
public void testDecorateSupplier() {
given(helloWorldService.returnHelloWorld()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Supplier<String> decoratedSupplier = Decorators
.ofSupplier(() -> helloWorldService.returnHelloWorld())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = decoratedSupplier.get();
assertThat(result).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorld();
}
@Test
public void shouldDelegateToOtherAdapter() {
String body = "this is from rxjava";
stubFor(get(urlPathEqualTo("/delegated"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBody(body)));
RetrofitService service = new Retrofit.Builder()
.addCallAdapterFactory(CircuitBreakerCallAdapter.of(circuitBreaker))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(wireMockRule.baseUrl())
.client(client)
.build()
.create(RetrofitService.class);
String resultBody = service.delegated().blockingGet();
assertThat(resultBody).isEqualTo(body);
verify(1, getRequestedFor(urlPathEqualTo("/delegated")));
final CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
}
@Test
public void testWithCircuitBreakerMetrics() throws Exception {
CircuitBreakerConfig config =
CircuitBreakerConfig.custom()
.waitDurationInOpenState(Duration.ofMillis(150))
.failureRateThreshold(50)
.permittedNumberOfCallsInHalfOpenState(3)
.slidingWindowSize(10)
.build();
CircuitBreaker circuitBreaker = CircuitBreakerRegistry.ofDefaults()
.circuitBreaker("test", config);
MetricRegistry metricRegistry = new MetricRegistry();
metricRegistry.registerAll(CircuitBreakerMetrics.ofCircuitBreaker(circuitBreaker));
circuitBreakerMetricsUsesFirstStateObjectInstance(circuitBreaker, metricRegistry);
}
@Test
public void testDecorateCallable() throws Exception {
given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Callable<String> decoratedCallable = Decorators
.ofCallable(() -> helloWorldService.returnHelloWorldWithException())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = decoratedCallable.call();
assertThat(result).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorldWithException();
}
@Test
public void shouldReportNewlyAddedCircuitBreaker() {
String name = "newBackend";
assertThat(registry.getSampleValue(
DEFAULT_CIRCUIT_BREAKER_STATE,
new String[]{"name", "state"},
new String[]{name, circuitBreaker.getState().name().toLowerCase()}
)).isNull();
CircuitBreaker newlyAddedCircuitBreaker = circuitBreakerRegistry.circuitBreaker(name);
double state = registry.getSampleValue(
DEFAULT_CIRCUIT_BREAKER_STATE,
new String[]{"name", "state"},
new String[]{name, newlyAddedCircuitBreaker.getState().name().toLowerCase()}
);
assertThat(state).isEqualTo(1);
}
@Test
public void shouldAddMetricsForANewlyCreatedCircuitBreaker() {
CircuitBreaker newCircuitBreaker = circuitBreakerRegistry.circuitBreaker("backendB");
newCircuitBreaker.onSuccess(0, TimeUnit.NANOSECONDS);
assertThat(taggedCircuitBreakerMetrics.meterIdMap).containsKeys("backendA", "backendB");
assertThat(taggedCircuitBreakerMetrics.meterIdMap.get("backendA")).hasSize(16);
assertThat(taggedCircuitBreakerMetrics.meterIdMap.get("backendB")).hasSize(16);
List<Meter> meters = meterRegistry.getMeters();
assertThat(meters).hasSize(32);
Collection<Gauge> gauges = meterRegistry.get(DEFAULT_CIRCUIT_BREAKER_BUFFERED_CALLS)
.gauges();
Optional<Gauge> successful = MetricsTestHelper
.findMeterByKindAndNameTags(gauges, "successful",
newCircuitBreaker.getName());
assertThat(successful).isPresent();
assertThat(successful.get().value())
.isEqualTo(newCircuitBreaker.getMetrics().getNumberOfSuccessfulCalls());
}
@Test
public void testDecorateCompletionStageWithCallNotPermittedExceptionFallback() throws ExecutionException, InterruptedException {
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
circuitBreaker.transitionToOpenState();
ThreadPoolBulkhead bulkhead = ThreadPoolBulkhead.ofDefaults("helloBackend");
CompletionStage<String> completionStage = Decorators
.ofSupplier(() -> helloWorldService.returnHelloWorld())
.withThreadPoolBulkhead(bulkhead)
.withCircuitBreaker(circuitBreaker)
.withFallback(CallNotPermittedException.class, (e) -> "Fallback")
.get();
String result = completionStage.toCompletableFuture().get();
assertThat(result).isEqualTo("Fallback");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfNotPermittedCalls()).isEqualTo(1);
}
@Test
public void testDecorateSupplierWithThreadPoolBulkhead()
throws ExecutionException, InterruptedException {
given(helloWorldService.returnHelloWorld()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
CompletableFuture<String> future = Decorators
.ofSupplier(() -> helloWorldService.returnHelloWorld())
.withThreadPoolBulkhead(ThreadPoolBulkhead.ofDefaults("helloBackend"))
.withTimeLimiter(TimeLimiter.ofDefaults(), Executors.newSingleThreadScheduledExecutor())
.withCircuitBreaker(circuitBreaker)
.get().toCompletableFuture();
String result = future.get();
assertThat(result).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorld();
}
@Test
public void testDecorateFunction() {
given(helloWorldService.returnHelloWorldWithName("Name")).willReturn("Hello world Name");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Function<String, String> decoratedFunction = Decorators
.ofFunction(helloWorldService::returnHelloWorldWithName)
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = decoratedFunction.apply("Name");
assertThat(result).isEqualTo("Hello world Name");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
}
private static Health.Builder addDetails(Health.Builder builder,
CircuitBreaker circuitBreaker) {
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
CircuitBreakerConfig config = circuitBreaker.getCircuitBreakerConfig();
builder.withDetail(FAILURE_RATE, metrics.getFailureRate() + "%")
.withDetail(FAILURE_RATE_THRESHOLD, config.getFailureRateThreshold() + "%")
.withDetail(SLOW_CALL_RATE, metrics.getSlowCallRate() + "%")
.withDetail(SLOW_CALL_RATE_THRESHOLD, config.getSlowCallRateThreshold() + "%")
.withDetail(BUFFERED_CALLS, metrics.getNumberOfBufferedCalls())
.withDetail(SLOW_CALLS, metrics.getNumberOfSlowCalls())
.withDetail(SLOW_FAILED_CALLS, metrics.getNumberOfSlowFailedCalls())
.withDetail(FAILED_CALLS, metrics.getNumberOfFailedCalls())
.withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls())
.withDetail(STATE, circuitBreaker.getState());
return builder;
}
@Test
public void shouldBufferErrorEvents() {
// tag::shouldBufferEvents[]
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
CircularEventConsumer<CircuitBreakerEvent> ringBuffer = new CircularEventConsumer<>(2);
circuitBreaker.getEventPublisher().onEvent(ringBuffer);
// end::shouldBufferEvents[]
assertThat(ringBuffer.getBufferedEvents()).isEmpty();
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException("Bla"));
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException("Bla"));
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException("Bla"));
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(3);
assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(3);
//Because capacity is 2
assertThat(ringBuffer.getBufferedEvents()).hasSize(2);
}
@Test
public void testDecorateCheckedFunction() throws IOException {
given(helloWorldService.returnHelloWorldWithNameWithException("Name"))
.willReturn("Hello world Name");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
CheckedFunction1<String, String> decoratedFunction = Decorators
.ofCheckedFunction(helloWorldService::returnHelloWorldWithNameWithException)
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = Try.of(() -> decoratedFunction.apply("Name")).get();
assertThat(result).isEqualTo("Hello world Name");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
}
@Test
public void testExecuteConsumer() {
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Decorators.DecorateConsumer<String> decoratedConsumer =
Decorators.ofConsumer((String input) -> helloWorldService
.sayHelloWorldWithName(input))
.withCircuitBreaker(circuitBreaker)
.withBulkhead(Bulkhead.ofDefaults("testName"))
.withRateLimiter(RateLimiter.ofDefaults("testName"));
decoratedConsumer.accept("test");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).sayHelloWorldWithName("test");
}
@Test
public void shouldRecordIOExceptionAsFailureAndBusinessExceptionAsSuccess() {
CircuitBreaker circuitBreaker = new CircuitBreakerStateMachine("testName", custom()
.slidingWindowSize(5)
.recordExceptions(IOException.class)
.build());
assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true);
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new IOException());
// Call 2 is a failure
assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true);
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new BusinessException("test"));
assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1);
assertThat(circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()).isEqualTo(1);
assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(2);
}
@Test
public void shouldIgnoreNumberFormatException() {
CircuitBreaker circuitBreaker = new CircuitBreakerStateMachine("testName", custom()
.failureRateThreshold(50)
.slidingWindowSize(5)
.waitDurationInOpenState(Duration.ofSeconds(5))
.ignoreExceptions(NumberFormatException.class)
.build());
assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true);
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException());
// Call 2 is a failure
assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true);
circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new NumberFormatException());
assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1);
assertThat(circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()).isEqualTo(0);
assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(1);
}
@Test
public void shouldRegisterMetrics() {
CircuitBreaker circuitBreaker = givenMetricRegistry(metricRegistry);
given(helloWorldService.returnHelloWorld()).willReturn("Hello world");
String value = circuitBreaker.executeSupplier(helloWorldService::returnHelloWorld);
assertThat(value).isEqualTo("Hello world");
then(helloWorldService).should(times(1)).returnHelloWorld();
assertThat(metricRegistry.getMetrics()).hasSize(10);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.state").getValue())
.isEqualTo(0);
assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.buffered")
.getValue()).isEqualTo(1);
assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.successful")
.getValue()).isEqualTo(1);
assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.failed")
.getValue()).isEqualTo(0);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.slow").getValue())
.isEqualTo(0);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.slow_successful")
.getValue()).isEqualTo(0);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.slow_failed")
.getValue()).isEqualTo(0);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.not_permitted")
.getValue()).isEqualTo(0L);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.failure_rate")
.getValue()).isEqualTo(-1f);
assertThat(
metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.slow_call_rate")
.getValue()).isEqualTo(-1f);
}
public Method findFallbackMethodInternal(Method method, Class<?> targetClass,
Class<? extends Throwable> throwableClass,
com.baidu.formula.circuitbreaker.annotation.CircuitBreaker annotation) {
for (; throwableClass != null && Throwable.class.isAssignableFrom(throwableClass);
throwableClass = (Class<? extends Throwable>) throwableClass.getSuperclass()) {
Class<?>[] parameterTypes = Stream.concat(
Arrays.stream(method.getParameterTypes()),
Stream.of(throwableClass)).toArray(Class<?>[]::new);
Method fallbackMethod = ReflectionUtils.findMethod(targetClass, annotation.fallback(), parameterTypes);
if (fallbackMethod != null) {
return fallbackMethod;
}
Class<?>[] interfaces = Arrays.stream(throwableClass.getInterfaces())
.filter(Throwable.class::isAssignableFrom)
.toArray(Class[]::new);
for (Class<?> i : interfaces) {
parameterTypes = Stream.concat(
Arrays.stream(method.getParameterTypes()),
Stream.of(i)).toArray(Class<?>[]::new);
fallbackMethod = ReflectionUtils.findMethod(targetClass, annotation.fallback(), parameterTypes);
if (fallbackMethod != null) {
return fallbackMethod;
}
}
}
return ReflectionUtils.findMethod(targetClass, annotation.fallback(), method.getParameterTypes());
}
@Bean
@Primary
public RegistryEventConsumer<CircuitBreaker> circuitBreakerRegistryEventConsumer(
Optional<List<RegistryEventConsumer<CircuitBreaker>>> optionalRegistryEventConsumers) {
return circuitBreakerConfiguration
.circuitBreakerRegistryEventConsumer(optionalRegistryEventConsumers);
}
@Override
public void bindTo(MeterRegistry registry) {
for (CircuitBreaker circuitBreaker : circuitBreakerRegistry.getAllCircuitBreakers()) {
addMetrics(registry, circuitBreaker);
}
circuitBreakerRegistry.getEventPublisher()
.onEntryAdded(event -> addMetrics(registry, event.getAddedEntry()));
circuitBreakerRegistry.getEventPublisher()
.onEntryRemoved(event -> removeMetrics(registry, event.getRemovedEntry().getName()));
circuitBreakerRegistry.getEventPublisher().onEntryReplaced(event -> {
removeMetrics(registry, event.getOldEntry().getName());
addMetrics(registry, event.getNewEntry());
});
}
@Test
public void telemetryDownEchoStillHealthyTest() throws Exception {
mockMvc.perform(get("/health")).andExpect(status().isOk());
// Simulate the stats endpoint going down, tripping the circuit breaker.
CircuitBreaker cb =
circuitBreakerRegistry.circuitBreaker(TelemetryEventListener.TELEMETRY_REGISTRY_NAME);
cb.transitionToOpenState();
mockMvc.perform(get("/health")).andExpect(status().isOk());
}
public <T> T handle(BackendService<T> backendService) {
if (this.circuitBreakerConfig == null) {
this.circuitBreakerConfig = circuitBreakerRegistry.getConfiguration(circuitBreakerL2cacheName)
.orElse(circuitBreakerRegistry.getDefaultConfig());
}
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(circuitBreakerL2cacheName,this.circuitBreakerConfig);
Supplier<T> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, backendService::handle);
if (fallbackStorage == null)
return Try.ofSupplier(decoratedSupplier).get();
return Try.ofSupplier(decoratedSupplier).recover(e -> handleException(e,backendService)).get();
}