BAEL-1165 Resilience4j (#4312)

* BAEL-1165 Resilience4j

* BAEL-1165 Resilience4j

Applied editor's suggestions
This commit is contained in:
Denis 2018-07-08 12:04:24 +03:00 committed by Grzegorz Piwowarek
parent 8fb345975d
commit 3b7cb37379
2 changed files with 158 additions and 0 deletions

View File

@ -705,6 +705,37 @@
<version>${jctools.version}</version>
</dependency>
<!-- resilience4j -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-cache</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-timelimiter</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
@ -951,6 +982,7 @@
<jets3t-version>0.9.4.0006L</jets3t-version>
<jctools.version>2.1.2</jctools.version>
<typesafe-akka.version>2.5.11</typesafe-akka.version>
<resilience4j.version>0.12.1</resilience4j.version>
<common-math3-version>3.6.1</common-math3-version>
<xchart-version>3.5.2</xchart-version>
<commons-net.version>3.6</commons-net.version>

View File

@ -0,0 +1,126 @@
package com.baeldung.resilience4j;
import io.github.resilience4j.bulkhead.Bulkhead;
import io.github.resilience4j.bulkhead.BulkheadConfig;
import io.github.resilience4j.bulkhead.BulkheadRegistry;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.junit.Before;
import org.junit.Test;
import java.time.Duration;
import java.util.concurrent.*;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
public class Resilience4jUnitTest {
interface RemoteService {
int process(int i);
}
private RemoteService service;
@Before
public void setUp() {
service = mock(RemoteService.class);
}
@Test
public void whenCircuitBreakerIsUsed_thenItWorksAsExpected() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
// Percentage of failures to start short-circuit
.failureRateThreshold(20)
// Min number of call attempts
.ringBufferSizeInClosedState(5)
.build();
CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
CircuitBreaker circuitBreaker = registry.circuitBreaker("my");
Function<Integer, Integer> decorated = CircuitBreaker.decorateFunction(circuitBreaker, service::process);
when(service.process(anyInt())).thenThrow(new RuntimeException());
for (int i = 0; i < 10; i++) {
try {
decorated.apply(i);
} catch (Exception ignore) {
}
}
verify(service, times(5)).process(any(Integer.class));
}
@Test
public void whenBulkheadIsUsed_thenItWorksAsExpected() throws InterruptedException {
BulkheadConfig config = BulkheadConfig.custom().maxConcurrentCalls(1).build();
BulkheadRegistry registry = BulkheadRegistry.of(config);
Bulkhead bulkhead = registry.bulkhead("my");
Function<Integer, Integer> decorated = Bulkhead.decorateFunction(bulkhead, service::process);
Future<?> taskInProgress = callAndBlock(decorated);
try {
assertThat(bulkhead.isCallPermitted()).isFalse();
} finally {
taskInProgress.cancel(true);
}
}
private Future<?> callAndBlock(Function<Integer, Integer> decoratedService) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
when(service.process(anyInt())).thenAnswer(invocation -> {
latch.countDown();
Thread.currentThread().join();
return null;
});
ForkJoinTask<?> result = ForkJoinPool.commonPool().submit(() -> {
decoratedService.apply(1);
});
latch.await();
return result;
}
@Test
public void whenRetryIsUsed_thenItWorksAsExpected() {
RetryConfig config = RetryConfig.custom().maxAttempts(2).build();
RetryRegistry registry = RetryRegistry.of(config);
Retry retry = registry.retry("my");
Function<Integer, Void> decorated = Retry.decorateFunction(retry, (Integer s) -> {
service.process(s);
return null;
});
when(service.process(anyInt())).thenThrow(new RuntimeException());
try {
decorated.apply(1);
fail("Expected an exception to be thrown if all retries failed");
} catch (Exception e) {
verify(service, times(2)).process(any(Integer.class));
}
}
@SuppressWarnings("unchecked")
@Test
public void whenTimeLimiterIsUsed_thenItWorksAsExpected() throws Exception {
long ttl = 1;
TimeLimiterConfig config = TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(ttl)).build();
TimeLimiter timeLimiter = TimeLimiter.of(config);
Future futureMock = mock(Future.class);
Callable restrictedCall = TimeLimiter.decorateFutureSupplier(timeLimiter, () -> futureMock);
restrictedCall.call();
verify(futureMock).get(ttl, TimeUnit.MILLISECONDS);
}
}