private CircuitBreakerMetrics(String prefix, Iterable<CircuitBreaker> circuitBreakers) { requireNonNull(prefix); requireNonNull(circuitBreakers); circuitBreakers.forEach((CircuitBreaker circuitBreaker) -> { String name = circuitBreaker.getName(); //state as an integer metricRegistry.register(name(prefix, name, STATE), (Gauge<Integer>)()-> circuitBreaker.getState().getOrder()); metricRegistry.register(name(prefix, name, SUCCESSFUL), (Gauge<Integer>) () -> circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()); metricRegistry.register(name(prefix, name, FAILED), (Gauge<Integer>) () -> circuitBreaker.getMetrics().getNumberOfFailedCalls()); metricRegistry.register(name(prefix, name, NOT_PERMITTED), (Gauge<Long>) () -> circuitBreaker.getMetrics().getNumberOfNotPermittedCalls()); metricRegistry.register(name(prefix, name, BUFFERED), (Gauge<Integer>) () -> circuitBreaker.getMetrics().getNumberOfBufferedCalls()); metricRegistry.register(name(prefix, name, BUFFERED_MAX), (Gauge<Integer>) () -> circuitBreaker.getMetrics().getMaxNumberOfBufferedCalls()); } ); }
@Test public void shouldInvokeAsyncApply() throws ExecutionException, InterruptedException { // tag::shouldInvokeAsyncApply[] // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // When Supplier<String> decoratedSupplier = CircuitBreaker .decorateSupplier(circuitBreaker, () -> "This can be any method which returns: 'Hello"); CompletableFuture<String> future = CompletableFuture.supplyAsync(decoratedSupplier) .thenApply(value -> value + " world'"); //Then assertThat(future.get()).isEqualTo("This can be any method which returns: 'Hello world'"); // end::shouldInvokeAsyncApply[] CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); }
final CircuitBreaker.State state = e._1; final String name = e._2; final double value = state == circuitBreaker.getState() ? 1.0 : 0.0;
@Test public void shouldReturnFailureWithRuntimeException() { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); }
@Test public void shouldDecorateCompletionStageAndReturnWithExceptionAtAsyncStage() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new RuntimeException("BAM! At async stage")); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld); Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = CircuitBreaker.decorateCompletionStage(circuitBreaker, completionStageSupplier); CompletionStage<String> decoratedCompletionStage = decoratedCompletionStageSupplier.get(); // Then the helloWorldService should be invoked 1 time assertThatThrownBy(decoratedCompletionStage.toCompletableFuture()::get) .isInstanceOf(ExecutionException.class).hasCause(new RuntimeException("BAM! At async stage")); BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); }
@Test public void shouldExecuteCompletionStageAndReturnWithSuccess() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello"); // When CompletionStage<String> decoratedCompletionStage = circuitBreaker .executeCompletionStage(() -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld)) .thenApply(value -> value + " world"); // Then the helloWorldService should be invoked 1 time assertThat(decoratedCompletionStage.toCompletableFuture().get()).isEqualTo("Hello world"); BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); }
@Test public void shouldDecorateCompletionStageAndReturnWithExceptionAtSyncStage() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService throws an exception // When Supplier<CompletionStage<String>> completionStageSupplier = () -> { throw new WebServiceException("BAM! At sync stage"); }; Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = CircuitBreaker.decorateCompletionStage(circuitBreaker, completionStageSupplier); Try<CompletionStage<String>> result = Try.of(decoratedCompletionStageSupplier::get); assertThat(result.isFailure()).isEqualTo(true); assertThat(result.failed().get()).isInstanceOf(WebServiceException.class); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); }
@Test public void shouldDecorateCompletionStageAndReturnWithSuccess() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello"); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld); Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = CircuitBreaker.decorateCompletionStage(circuitBreaker, completionStageSupplier); CompletionStage<String> decoratedCompletionStage = decoratedCompletionStageSupplier .get() .thenApply(value -> value + " world"); // Then the helloWorldService should be invoked 1 time assertThat(decoratedCompletionStage.toCompletableFuture().get()).isEqualTo("Hello world"); BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); }
@Test public void shouldThrowCircuitBreakerOpenException() { // tag::shouldThrowCircuitBreakerOpenException[] // Given CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .ringBufferSizeInClosedState(2) .waitDurationInOpenState(Duration.ofMillis(1000)) .build(); CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); // Simulate a failure attempt circuitBreaker.onError(0, new RuntimeException()); // CircuitBreaker is still CLOSED, because 1 failure is allowed assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Simulate a failure attempt circuitBreaker.onError(0, new RuntimeException()); // CircuitBreaker is OPEN, because the failure rate is above 50% assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN); // When I decorate my function and invoke the decorated function Try<String> result = Try.of(CircuitBreaker.decorateCheckedSupplier(circuitBreaker, () -> "Hello")) .map(value -> value + " world"); // Then the call fails, because CircuitBreaker is OPEN assertThat(result.isFailure()).isTrue(); // Exception is CircuitBreakerOpenException assertThat(result.failed().get()).isInstanceOf(CircuitBreakerOpenException.class); // end::shouldThrowCircuitBreakerOpenException[] CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(2); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(2); }
assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(2);
private Health mapBackendMonitorState(CircuitBreaker circuitBreaker) { switch (circuitBreaker.getState()) { case CLOSED: return addDetails(Health.up(), circuitBreaker).build(); case OPEN: return addDetails(Health.down(), circuitBreaker).build(); case HALF_OPEN: return addDetails(Health.unknown(),circuitBreaker).build(); default: return addDetails(Health.unknown(), circuitBreaker).build(); } }
@Test public void shouldReturnFailureWithCircuitBreakerOpenException() { // Given // Create a custom configuration for a CircuitBreaker CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .ringBufferSizeInClosedState(2) .ringBufferSizeInHalfOpenState(2) .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofMillis(1000)) .build(); // Create a CircuitBreakerRegistry with a custom global configuration CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); circuitBreaker.onError(0, new RuntimeException()); circuitBreaker.onError(0, new RuntimeException()); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(2); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(2); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(CircuitBreakerOpenException.class); }
when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED);
private Health mapBackendMonitorState(CircuitBreaker circuitBreaker) { switch (circuitBreaker.getState()) { case CLOSED: return addDetails(Health.up(), circuitBreaker).build(); case OPEN: return addDetails(Health.down(), circuitBreaker).build(); case HALF_OPEN: return addDetails(Health.unknown(),circuitBreaker).build(); default: return addDetails(Health.unknown(), circuitBreaker).build(); } }
assertThat(circuitBreaker.getState(), equalTo(CircuitBreaker.State.CLOSED)); assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls(), equalTo(0)); assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls(), equalTo(0)); assertThat(circuitBreaker.getState(), equalTo(CircuitBreaker.State.CLOSED)); assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls(), equalTo(1)); assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls(), equalTo(1)); assertThat(circuitBreaker.getState(), equalTo(CircuitBreaker.State.OPEN)); assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls(), equalTo(10)); assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls(), equalTo(10)); .until(() -> { circuitBreaker.isCallPermitted(); return circuitBreaker.getState().equals(CircuitBreaker.State.HALF_OPEN); }); assertThat(circuitBreaker.getState(), equalTo(CircuitBreaker.State.HALF_OPEN)); assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls(), equalTo(1)); assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls(), equalTo(0)); assertThat(gauges.get("resilience4j.circuitbreaker.test.successful").getValue(), equalTo(1)); circuitBreaker.onSuccess(0); assertThat(circuitBreaker.getState(), equalTo(CircuitBreaker.State.HALF_OPEN)); assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls(), equalTo(2)); assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls(), equalTo(0)); assertThat(gauges.get("resilience4j.circuitbreaker.test.successful").getValue(), equalTo(2)); circuitBreaker.onSuccess(0);
when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED);
@Override public void bindTo(MeterRegistry registry) { for (CircuitBreaker circuitBreaker : circuitBreakers) { final String name = circuitBreaker.getName(); Gauge.builder(getName(prefix, name, STATE), circuitBreaker, (cb) -> cb.getState().getOrder()) .register(registry); Gauge.builder(getName(prefix, name, BUFFERED_MAX), circuitBreaker, (cb) -> cb.getMetrics().getMaxNumberOfBufferedCalls()) .register(registry); Gauge.builder(getName(prefix, name, BUFFERED), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfBufferedCalls()) .register(registry); Gauge.builder(getName(prefix, name, FAILED), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfFailedCalls()) .register(registry); Gauge.builder(getName(prefix, name, NOT_PERMITTED), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfNotPermittedCalls()) .register(registry); Gauge.builder(getName(prefix, name, SUCCESSFUL), circuitBreaker, (cb) -> cb.getMetrics().getNumberOfSuccessfulCalls()) .register(registry); } } }