Java `Fault Tolerance` (`Resilience4j`, `Hystrix`) `Circuit Breaker`, `Rate Limiter`, `Retry`

各位观众老爷,大家好!今天咱们来聊聊Java世界里的“容错三剑客”——Circuit Breaker(断路器)、Rate Limiter(限流器)和Retry(重试)。这哥仨啊,就像咱们厨房里的保险丝、水龙头和备用食材,关键时刻能保你系统不死机、不崩溃、还能帮你“起死回生”。

别害怕,今天咱不用那些高大上的学术名词,就用大白话、接地气的例子,把这几个家伙扒个底朝天,再手把手教你用代码把它们武装到你的Java程序里。

一、背景故事:为啥需要容错?

想象一下,你开了一家餐厅(你的Java应用),每天顾客盈门(用户请求)。

  • 情况一:后厨着火了(下游服务挂了)。如果你的服务直接崩溃,所有顾客都得饿肚子,老板(你)得赔钱!
  • 情况二:来了个大胃王(恶意请求)。一个人吃光了所有食材,后面来的顾客啥都吃不着,差评如潮!
  • 情况三:厨师偶尔失手(网络抖动)。菜做砸了,顾客很不爽,下次可能就不来了。

所以,我们需要一些机制来应对这些突发情况,保证餐厅(应用)的稳定运营。这就是容错的意义所在。

二、容错三剑客登场

  1. Circuit Breaker(断路器):防火墙

    断路器就像你家里的保险丝,当电路过载(下游服务故障)时,它会自动跳闸(熔断),防止整个电路烧毁(应用崩溃)。过一段时间,它会尝试重新闭合(半开状态),看看电路是否恢复正常。

    • 状态

      | 状态 | 描述 |
      | ——– | —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————-.

      • Closed(关闭): 断路器关闭状态,所有请求都允许通过。
      • Open(打开): 断路器打开状态,所有请求都被快速失败。
      • Half-Open(半开):断路器尝试恢复状态,允许部分请求通过,如果请求成功,则恢复到关闭状态,否则保持打开状态。
    • 关键参数

      • failureRateThreshold:失败率阈值,当失败率超过这个值,断路器会打开。
      • minimumNumberOfCalls:最小请求数,只有当请求数达到这个值,才会计算失败率。
      • slidingWindowSize:滑动窗口大小,用于计算失败率的窗口大小。
      • permittedCallsInHalfOpenState:半开状态下允许通过的请求数。
      • waitDurationInOpenState:断路器打开后,等待多长时间进入半开状态。
    • 代码示例 (使用Resilience4j)

      import io.github.resilience4j.circuitbreaker.CircuitBreaker;
      import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
      import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
      import io.vavr.CheckedFunction0;
      import io.vavr.control.Try;
      
      import java.time.Duration;
      import java.util.function.Supplier;
      
      public class CircuitBreakerExample {
      
          public static void main(String[] args) {
      
              // 配置断路器
              CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                      .failureRateThreshold(50) // 失败率超过50%就打开断路器
                      .minimumNumberOfCalls(10) // 至少10次调用才开始计算失败率
                      .slidingWindowSize(10) // 滑动窗口大小为10
                      .waitDurationInOpenState(Duration.ofSeconds(5)) // 打开状态持续5秒
                      .permittedCallsInHalfOpenState(2) // 半开状态允许2次调用
                      .build();
      
              // 创建断路器注册器
              CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
      
              // 获取一个断路器实例
              CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("myCircuitBreaker");
      
              // 定义一个可能失败的服务
              Supplier<String> serviceCall = () -> {
                  // 模拟服务调用,有时成功,有时失败
                  if (Math.random() > 0.5) {
                      System.out.println("Service call successful!");
                      return "Service response";
                  } else {
                      System.out.println("Service call failed!");
                      throw new RuntimeException("Service failed");
                  }
              };
      
              // 使用断路器装饰服务调用
              Supplier<String> decoratedServiceCall = CircuitBreaker.decorateSupplier(circuitBreaker, serviceCall);
      
              // 调用服务,并处理可能出现的异常
              for (int i = 0; i < 20; i++) {
                  try {
                      String result = decoratedServiceCall.get();
                      System.out.println("Result: " + result);
                  } catch (Exception e) {
                      System.out.println("Circuit Breaker: " + circuitBreaker.getState());
                      System.out.println("Exception: " + e.getMessage());
                  }
                  try {
                      Thread.sleep(500); // 模拟调用间隔
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }

      解释:

      1. 配置断路器: CircuitBreakerConfig 定义了断路器的行为,例如失败率阈值、最小调用次数、滑动窗口大小等等。
      2. 创建断路器注册器: CircuitBreakerRegistry 用于管理多个断路器实例。
      3. 获取断路器实例: circuitBreakerRegistry.circuitBreaker("myCircuitBreaker") 根据名称获取一个断路器实例。
      4. 定义服务调用: serviceCall 是一个 Supplier,它封装了实际的服务调用逻辑。在这里,我们模拟了一个可能成功或失败的服务。
      5. 装饰服务调用: CircuitBreaker.decorateSupplier(circuitBreaker, serviceCall) 使用断路器来包装服务调用。 当服务调用失败率达到配置的阈值时,断路器会打开,并阻止后续的调用。
      6. 调用服务并处理异常: 在循环中,我们调用装饰后的服务,并捕获可能抛出的异常。 当断路器打开时,会抛出 io.github.resilience4j.circuitbreaker.CallNotPermittedException 异常。

      代码解释说明:

      • io.vavr.CheckedFunction0io.vavr.control.Try 是 Vavr 库中的类,用于简化函数式编程和异常处理。
      • CheckedFunction0 类似于 java.util.function.Supplier,但允许抛出受检异常。
      • Try 类似于 java.util.Optional,但用于处理可能抛出异常的操作。 它可以是 Success(成功)或 Failure(失败)。
      • Resilience4j 提供了与 Vavr 的集成,可以使用 Try 来处理服务调用结果。 例如,可以使用 Try.of(serviceCall) 来包装服务调用,然后使用 TryonSuccessonFailure 方法来处理成功和失败的情况。

      总结:

      • 断路器是保护系统免受级联故障的重要工具。
      • Resilience4j 提供了易于使用的 API 来配置和使用断路器。
      • 合理配置断路器的参数对于系统的稳定性和可用性至关重要。
  2. Rate Limiter(限流器):水龙头

    限流器就像你家的水龙头,它可以控制单位时间内通过的请求数量,防止系统被过多的请求压垮。

    • 算法

      • 令牌桶(Token Bucket): 想象一个桶里装满了令牌,每个请求都需要拿到一个令牌才能通过。 桶里的令牌会以一定的速率生成,如果桶满了,新的令牌就会被丢弃。
      • 漏桶(Leaky Bucket): 想象一个桶以恒定的速率漏水,每个请求都相当于往桶里加水。 如果桶满了,新的请求就会被丢弃。
      • 固定窗口计数器(Fixed Window Counter): 在固定的时间窗口内,记录请求的数量。 如果请求数量超过阈值,则拒绝后续的请求。
      • 滑动窗口计数器(Sliding Window Counter): 类似于固定窗口计数器,但窗口是滑动的,可以更精确地控制请求速率。
    • 关键参数

      • limitForPeriod:时间段内的最大请求数。
      • limitRefreshPeriod:时间段的持续时间。
      • timeoutDuration:获取许可的超时时间。
    • 代码示例 (使用Resilience4j)

      import io.github.resilience4j.ratelimiter.RateLimiter;
      import io.github.resilience4j.ratelimiter.RateLimiterConfig;
      import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
      
      import java.time.Duration;
      
      public class RateLimiterExample {
      
          public static void main(String[] args) {
      
              // 配置限流器
              RateLimiterConfig config = RateLimiterConfig.custom()
                      .limitForPeriod(10) // 每10秒允许10个请求
                      .limitRefreshPeriod(Duration.ofSeconds(10)) // 刷新周期为10秒
                      .timeoutDuration(Duration.ofSeconds(1)) // 获取许可的超时时间为1秒
                      .build();
      
              // 创建限流器注册器
              RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config);
      
              // 获取一个限流器实例
              RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter("myRateLimiter");
      
              // 定义一个需要限流的服务
              Runnable serviceCall = () -> {
                  System.out.println("Service call successful!");
              };
      
              // 使用限流器装饰服务调用
              Runnable decoratedServiceCall = RateLimiter.decorateRunnable(rateLimiter, serviceCall);
      
              // 调用服务,并检查是否被限流
              for (int i = 0; i < 20; i++) {
                  try {
                      rateLimiter.executeRunnable(decoratedServiceCall);
                      System.out.println("Request " + i + " passed.");
                  } catch (io.github.resilience4j.ratelimiter.RequestNotPermitted e) {
                      System.out.println("Request " + i + " blocked by rate limiter.");
                  }
                  try {
                      Thread.sleep(500); // 模拟调用间隔
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }

      解释:

      1. 配置限流器: RateLimiterConfig 定义了限流器的行为,例如时间段内的最大请求数、刷新周期和获取许可的超时时间。
      2. 创建限流器注册器: RateLimiterRegistry 用于管理多个限流器实例。
      3. 获取限流器实例: rateLimiterRegistry.rateLimiter("myRateLimiter") 根据名称获取一个限流器实例。
      4. 定义服务调用: serviceCall 是一个 Runnable,它封装了实际的服务调用逻辑。
      5. 装饰服务调用: RateLimiter.decorateRunnable(rateLimiter, serviceCall) 使用限流器来包装服务调用。 当请求速率超过配置的限制时,限流器会阻止后续的调用。
      6. 调用服务并检查是否被限流: 在循环中,我们调用装饰后的服务,并捕获可能抛出的 RequestNotPermitted 异常。 当请求被限流时,会抛出此异常。

      总结:

      • 限流器是保护系统免受过载的重要工具。
      • Resilience4j 提供了易于使用的 API 来配置和使用限流器。
      • 合理配置限流器的参数对于系统的稳定性和可用性至关重要。
  3. Retry(重试):备用食材

    重试机制就像你厨房里的备用食材,当第一次做菜失败时(服务调用失败),你可以尝试重新做一次(重试)。

    • 策略

      • 固定间隔重试: 每次重试之间等待固定的时间。
      • 指数退避重试: 每次重试之间等待的时间呈指数增长。 例如,第一次等待 1 秒,第二次等待 2 秒,第三次等待 4 秒,以此类推。
      • 随机退避重试: 在指数退避的基础上,增加一个随机的抖动,避免多个客户端同时重试。
    • 关键参数

      • maxAttempts:最大重试次数。
      • waitDuration:重试之间的等待时间。
      • retryOnException:指定哪些异常需要重试。
      • retryOnResult:指定哪些结果需要重试。
    • 代码示例 (使用Resilience4j)

      import io.github.resilience4j.retry.Retry;
      import io.github.resilience4j.retry.RetryConfig;
      import io.github.resilience4j.retry.RetryRegistry;
      
      import java.time.Duration;
      import java.util.function.Supplier;
      
      public class RetryExample {
      
          public static void main(String[] args) {
      
              // 配置重试
              RetryConfig config = RetryConfig.custom()
                      .maxAttempts(3) // 最大重试3次
                      .waitDuration(Duration.ofSeconds(1)) // 每次重试等待1秒
                      .retryOnException(e -> e instanceof RuntimeException) // 只有RuntimeException才重试
                      .build();
      
              // 创建重试注册器
              RetryRegistry retryRegistry = RetryRegistry.of(config);
      
              // 获取一个重试实例
              Retry retry = retryRegistry.retry("myRetry");
      
              // 定义一个可能失败的服务
              Supplier<String> serviceCall = () -> {
                  // 模拟服务调用,有时成功,有时失败
                  if (Math.random() > 0.5) {
                      System.out.println("Service call successful!");
                      return "Service response";
                  } else {
                      System.out.println("Service call failed!");
                      throw new RuntimeException("Service failed");
                  }
              };
      
              // 使用重试装饰服务调用
              Supplier<String> decoratedServiceCall = Retry.decorateSupplier(retry, serviceCall);
      
              // 调用服务,并处理可能出现的异常
              try {
                  String result = decoratedServiceCall.get();
                  System.out.println("Result: " + result);
              } catch (Exception e) {
                  System.out.println("Exception: " + e.getMessage());
              }
          }
      }

      解释:

      1. 配置重试: RetryConfig 定义了重试的行为,例如最大重试次数、重试之间的等待时间以及需要重试的异常类型。
      2. 创建重试注册器: RetryRegistry 用于管理多个重试实例。
      3. 获取重试实例: retryRegistry.retry("myRetry") 根据名称获取一个重试实例。
      4. 定义服务调用: serviceCall 是一个 Supplier,它封装了实际的服务调用逻辑。在这里,我们模拟了一个可能成功或失败的服务。
      5. 装饰服务调用: Retry.decorateSupplier(retry, serviceCall) 使用重试来包装服务调用。 当服务调用失败时,重试会根据配置的策略自动重试。
      6. 调用服务并处理异常:try-catch 块中,我们调用装饰后的服务,并捕获可能抛出的异常。

      总结:

      • 重试是提高系统可靠性的重要工具。
      • Resilience4j 提供了易于使用的 API 来配置和使用重试。
      • 合理配置重试的参数对于避免无限重试和性能问题至关重要。

三、Hystrix vs. Resilience4j

在Java容错领域,Hystrix曾经是当红炸子鸡,但现在Resilience4j后来居上,大有取而代之的趋势。为啥呢?

特性 Hystrix Resilience4j
维护状态 Netflix已经停止维护Hystrix,建议迁移到其他方案。 Resilience4j仍在积极维护和更新。
依赖 Hystrix依赖于Archaius,Archaius是Netflix的配置管理库,这增加了项目的复杂性。 Resilience4j是轻量级的,只依赖于Java 8+标准库和Vavr(一个函数式编程库)。
编程模型 Hystrix使用自己的命令模式,需要将服务调用包装成HystrixCommand或HystrixObservableCommand。 Resilience4j使用装饰器模式,可以方便地将容错策略应用到任何Java函数或方法上。
模块化 Hystrix将所有容错策略都集成在一个库中,耦合度较高。 Resilience4j是模块化的,每个容错策略(断路器、限流器、重试等)都是一个独立的模块,可以根据需要选择使用。
函数式编程支持 Hystrix对函数式编程的支持有限。 Resilience4j对函数式编程有很好的支持,可以与Java 8的函数式接口和Vavr库无缝集成。
Spring Boot集成 Hystrix通过@HystrixCommand注解与Spring Boot集成,使用起来比较方便。 Resilience4j也提供了Spring Boot集成,可以使用@CircuitBreaker@RateLimiter@Retry等注解来声明式地应用容错策略。
社区 Hystrix的社区活跃度较低。 Resilience4j的社区活跃度较高,有更多的开发者参与贡献和维护。

简单来说,Resilience4j更轻量级、更模块化、对函数式编程支持更好,而且还在持续更新,更符合现代微服务架构的需求。

四、最佳实践

  1. 不要过度使用容错: 容错机制会增加系统的复杂性,只有在真正需要的时候才使用。
  2. 合理配置参数:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注