案例概述在本文中,我们讨论一下Resilience4j库。该库通过管理远程通信的容错性来帮助实现弹性系统。这个库受到Hystrix的启发,但提供了更方便的API和许多其他特性,如速率限制器(阻塞太频繁的请求)、Bulkhead(避免太多并发请求)等。Maven设置首先,我们需要将目标模块添加到我们的pom.xml中(例如,我们添加了Circuit Breaker):<dependency>    <groupId>io.github.resilience4j</groupId>    <artifactId>resilience4j-circuitbreaker</artifactId>    <version>0.12.1</version></dependency>在这里,我们使用的是断路器模块。所有模块及其最新版本均可在Maven Central上找到。在接下来的部分中,我们将介绍库中最常用的模块。断路器请注意,对于此模块,我们需要上面显示的设置resilience4j-circuitbreaker依赖项。断路器模式可以帮助我们在远程服务中断时防止一连串的故障。在多次失败的尝试之后,我们可以认为服务不可用/重载,并急切地拒绝所有后续的请求。通过这种方式,我们可以为可能失败的调用节省系统资源。让我们看看我们如何通过Resilience4j实现这一目标。首先,我们需要定义要使用的设置。最简单的方法是使用默认设置:CircuitBreakerRegistry circuitBreakerRegistry  = CircuitBreakerRegistry.ofDefaults();也可以使用自定义参数:CircuitBreakerConfig config = CircuitBreakerConfig.custom()  .failureRateThreshold(20)  .ringBufferSizeInClosedState(5)  .build();在这里,我们将速率阈值设置为20%,并将尝试呼叫设置为最少5次。然后,我们创建一个CircuitBreaker对象并通过它调用远程服务:interface RemoteService {    int process(int i);} CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);CircuitBreaker circuitBreaker = registry.circuitBreaker(“my”);Function<Integer, Integer> decorated = CircuitBreaker  .decorateFunction(circuitBreaker, service::process);最后,让我们通过JUnit测试看看它是如何工作的。我们将尝试调用服务10次。我们应该能够验证该呼叫至少尝试了5次,然后在20%的呼叫失败后停止:when(service.process(any(Integer.class))).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));断路器的状态和设置断路器可以处于以下三种状态之一:CLOSED - 一切正常,不涉及短路OPEN - 远程服务器已关闭,所有请求都被短路HALF_OPEN - 从进入开放状态到现在已经经过了一段时间,断路器允许请求检查远程服务是否重新上线我们可以配置以下设置:断路器打开并开始短路呼叫的故障率阈值等待时间,它定义了断路器在切换到半开状态之前应该保持打开状态的时间断路器半开或半闭时环形缓冲器的尺寸处理断路器事件的定制电路断路器事件监听器一个自定义谓词,用于评估异常是否应算作故障,从而提高故障率速率限制器与上一节类似,此功能需要resilience4j-ratelimiter依赖项。顾名思义,此功能允许限制对某些服务的访问。它的API与CircuitBreaker非常相似- 有Registry,Config和Limiter类。以下是它的示例:RateLimiterConfig config = RateLimiterConfig.custom().limitForPeriod(2).build();RateLimiterRegistry registry = RateLimiterRegistry.of(config);RateLimiter rateLimiter = registry.rateLimiter(“my”);Function<Integer, Integer> decorated  = RateLimiter.decorateFunction(rateLimiter, service::process);现在,如果需要的话,所有对已修饰的服务块的调用都要符合速率限制器配置。我们可以配置如下参数:限制刷新的时间段刷新周期的权限限制默认等待权限持续时间Bulkhead在这里,我们首先需要relasticience4j-bulkhead依赖项。可以限制对特定服务的并发调用数。让我们看一个使用Bulkhead API配置最多一个并发调用的示例: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);要测试此配置,我们将调用模拟服务的方法。然后,我们确保Bulkhead不允许任何其他调用:CountDownLatch latch = new CountDownLatch(1);when(service.process(anyInt())).thenAnswer(invocation -> {    latch.countDown();    Thread.currentThread().join();    return null;}); ForkJoinTask<?> task = ForkJoinPool.commonPool().submit(() -> {    try {        decorated.apply(1);    } finally {        bulkhead.onComplete();    }});latch.await();assertThat(bulkhead.isCallPermitted()).isFalse();我们可以配置以下设置:Bulkhead允许的最大并行执行量尝试进入饱和舱壁时线程等待的最长时间重试对于此功能,我们需要将resilience4j-retry库添加到项目中。我们可以使用Retry API 自动重试失败的呼叫: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));}我们还可以配置以下内容:最大尝试次数重试前的等待时间自定义函数,用于修改失败后的等待间隔自定义谓词,用于评估异常是否应导致重试调用缓存Cache模块需要resilience4j-cache依赖项。初始化看起来与其他模块略有不同:javax.cache.Cache cache = …; // Use appropriate cache hereCache<Integer, Integer> cacheContext = Cache.of(cache);Function<Integer, Integer> decorated  = Cache.decorateSupplier(cacheContext, () -> service.process(1));这里的缓存是通过使用JSR-107 Cache实现完成的,Resilience4j提供了一种应用它的方法。请注意,没有用于装饰功能的API(如Cache.decorateFunction(Function)),API仅支持 Supplier和Callable类型。TimeLimiter对于此模块,我们必须添加resilience4j-timelimiter依赖项。可以使用TimeLimiter限制调用远程服务所花费的时间。为了演示,让我们设置一个配置超时为1毫秒的TimeLimiter:long ttl = 1;TimeLimiterConfig config  = TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(ttl)).build();TimeLimiter timeLimiter = TimeLimiter.of(config);接下来,让我们验证Resilience4j是否使用预期的超时调用Future.get():Future futureMock = mock(Future.class);Callable restrictedCall  = TimeLimiter.decorateFutureSupplier(timeLimiter, () -> futureMock);restrictedCall.call(); verify(futureMock).get(ttl, TimeUnit.MILLISECONDS);我们也可以将它与CircuitBreaker结合使用:Callable chainedCallable  = CircuitBreaker.decorateCallable(circuitBreaker, restrictedCall);附加模块Resilience4j还提供了许多附加模块,可以简化与流行框架和库的集成。一些比较知名的集成是:Spring Boot – resilience4j-spring-bootRatpack – resilience4j-ratpackRetrofit – resilience4j-retrofitVertx – resilience4j-vertxDropwizard – resilience4j-metricsPrometheus – resilience4j-prometheus案例结论在本文中,我们了解了Resilience4j库的各个方面,并学习了如何使用它来解决服务器间通信中的各种容错问题。