一、Resilience4j 介绍

在微服务架构中,网络波动、服务宕机是不可避免的。为了保证系统的高可用性,我们需要引入容错机制。Resilience4j 是目前 Java 生态中最受欢迎的容错框架之一。Resilience4j提供了一系列模块化的组件,如断路器(Circuit Breaker)、重试(Retry)、限流器(Rate Limiter)、隔离器(Bulkhead)、缓存(Cache)以及时间器(TimeLimiter),这些组件可以单独使用或组合使用,以应对不同类型的故障场景。
本文将基于一个案例,介绍如何通过 R4jRegistry 和 R4jManager 两个核心类,优雅地管理熔断器(CircuitBreaker)与重试(Retry)机制。

二、Resilience4j实现案例

本方案包含两个核心组件,分别解决了“组件管理”与“任务执行”的问题:

1.创建maven项目、引入pom依赖、配置applicationContext.xml文件

pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>java_stu</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lb</groupId>
    <artifactId>sf_stu</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- Resilience4j CircuitBreaker -->
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-circuitbreaker</artifactId>
            <version>2.1.0</version>
        </dependency>

        <!-- Resilience4j Retry -->
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-retry</artifactId>
            <version>2.1.0</version>
        </dependency>

        <!-- Spring Expression (required by Spring Context 5.x) -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.31</version>
        </dependency>

        <!-- Spring Core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.31</version>
        </dependency>

        <!-- Spring Core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.31</version>
        </dependency>

        <!-- Spring Beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.31</version>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>

        <!-- Apache Commons Lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.14.0</version>
        </dependency>

        <!-- SLF4J Simple Logger (Fixes "Failed to load class StaticLoggerBinder" warning) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.36</version>
        </dependency>
    </dependencies>

</project>

applicationContext.xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 1. 配置熔断器注册表 -->
    <bean id="circuitBreakerRegistry" class="io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry" factory-method="of">
        <constructor-arg>
            <bean class="io.github.resilience4j.circuitbreaker.CircuitBreakerConfig" factory-method="ofDefaults"/>
        </constructor-arg>
    </bean>

    <!-- 2. 配置重试注册表 -->
    <bean id="retryRegistry" class="io.github.resilience4j.retry.RetryRegistry" factory-method="of">
        <constructor-arg>
            <bean class="io.github.resilience4j.retry.RetryConfig" factory-method="ofDefaults"/>
        </constructor-arg>
    </bean>

    <!-- 3. 配置具体的熔断器实例(供 R4jRegistry.circuitBreaker() 获取) -->
    <bean id="myCircuitBreaker" class="io.github.resilience4j.circuitbreaker.CircuitBreaker"
          factory-bean="circuitBreakerRegistry" factory-method="circuitBreaker">
        <constructor-arg value="myCircuitBreaker"/>
    </bean>

    <!-- 4. 配置具体的重试实例(供 R4jRegistry.retry() 获取) -->
    <bean id="registryRetry" class="io.github.resilience4j.retry.Retry"
          factory-bean="retryRegistry" factory-method="retry">
        <constructor-arg value="registryRetry"/>
    </bean>

</beans>

2. R4jRegistry:组件注册表
R4jRegistry 负责从 Spring IoC 容器中获取并缓存 Resilience4j 的实例。
作用:统一管理全局的 CircuitBreaker 和 Retry 实例。
特点:支持通过名称不区分大小写地查找组件,非常适合企业级多模块共享配置。

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryRegistry;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * R4jRegistry - Resilience4j 注册表工具类
 * 
 * <p>该类提供从 Spring IoC 容器中获取 Resilience4j 熔断器和重试实例的便捷方法。
 * 通过静态代码块初始化,确保在类加载时完成注册表的获取。
 * 
 * <p>主要功能:
 * <ul>
 *     <li>获取 CircuitBreaker(熔断器)实例</li>
 *     <li>获取 Retry(重试)实例</li>
 *     <li>通过名称不区分大小写的方式查找对应的实例</li>
 * </ul>
 * 
 * <p>使用示例:
 * <pre>
 * {@code
 * // 获取熔断器实例
 * CircuitBreaker circuitBreaker = R4jRegistry.circuitBreaker("myService");
 * 
 * // 获取重试实例
 * Retry retry = R4jRegistry.retry("myRetry");
 * }
 * </pre>
 * 
 * @author LS
 * @see io.github.resilience4j.circuitbreaker.CircuitBreaker
 * @see io.github.resilience4j.retry.Retry
 */
public class R4jRegistry {

    /** 熔断器注册表,用于管理所有的 CircuitBreaker 实例 */
    private static final CircuitBreakerRegistry circuitBreakerRegistry;
    
    /** 重试注册表,用于管理所有的 Retry 实例 */
    private static final RetryRegistry retryRegistry;

    /**
     * 静态代码块:在类加载时初始化注册表
     * 
     * <p>通过 ClassPathXmlApplicationContext 加载 applicationContext.xml 配置文件,
     * 从 Spring IoC 容器中获取 CircuitBreakerRegistry 和 RetryRegistry 的 Bean 实例。
     * 
     * <p>注意事项:
     * <ul>
     *     <li>确保类路径下存在 applicationContext.xml 配置文件</li>
     *     <li>配置文件中需正确配置 CircuitBreakerRegistry 和 RetryRegistry Bean</li>
     * </ul>
     */
    static {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        circuitBreakerRegistry = context.getBean(CircuitBreakerRegistry.class);
        retryRegistry = context.getBean(RetryRegistry.class);
    }

    /**
     * 私有构造函数,防止实例化
     * 
     * <p>该类为工具类,所有方法均为静态方法,无需创建实例。
     */
    private R4jRegistry() {
    }

    /**
     * 根据名称获取熔断器实例
     * 
     * <p>从所有注册的 CircuitBreaker 中查找与指定名称匹配的实例(不区分大小写)。
     * 
     * @param name 熔断器名称
     * @return 匹配的 CircuitBreaker 实例,如果未找到则返回 null
     * 
     * <p>查找逻辑:
     * <ol>
     *     <li>获取所有已注册的 CircuitBreaker</li>
     *     <li>使用 Stream API 过滤出名称匹配的实例</li>
     *     <li>返回第一个匹配的结果,未找到则返回 null</li>
     * </ol>
     * 
     * @see io.github.resilience4j.circuitbreaker.CircuitBreaker
     */
    public static CircuitBreaker circuitBreaker(String name) {
        return circuitBreakerRegistry.getAllCircuitBreakers().stream()
                .filter(circuitBreaker -> StringUtils.equalsIgnoreCase(circuitBreaker.getName(), name))
                .findFirst()
                .orElse(null);
    }

    /**
     * 根据名称获取重试实例
     * 
     * <p>从所有注册的 Retry 中查找与指定名称匹配的实例(不区分大小写)。
     * 
     * @param name 重试实例名称
     * @return 匹配的 Retry 实例,如果未找到则返回 null
     * 
     * <p>查找逻辑:
     * <ol>
     *     <li>获取所有已注册的 Retry</li>
     *     <li>使用 Stream API 过滤出名称匹配的实例</li>
     *     <li>返回第一个匹配的结果,未找到则返回 null</li>
     * </ol>
     * 
     * @see io.github.resilience4j.retry.Retry
     */
    public static Retry retry(String name) {
        return retryRegistry.getAllRetries().stream()
                .filter(retry -> StringUtils.equalsIgnoreCase(retry.getName(), name))
                .findFirst()
                .orElse(null);
    }
}

3. R4jManager:任务管理器
R4jManager 基于装饰器模式实现,它将容错逻辑与业务逻辑解耦。
作用:通过链式调用 acquire() 添加组件,然后调用 run() 方法自动织入容错逻辑。
优势:支持多种函数式接口(Runnable, Consumer, Supplier, Function),满足各种方法签名需求。

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.retry.Retry;
import lombok.SneakyThrows;

import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * R4jManager - Resilience4j 管理器
 * 
 * <p>该类提供对 Resilience4j 组件(熔断器、重试)的统一管理和执行功能。
 * 通过链式调用添加多个组件,然后使用相应的 run 方法执行目标操作,
 * 自动应用所有已添加的组件逻辑。
 * 
 * <p>核心设计:
 * <ul>
 *     <li>使用栈(Stack)存储添加的组件,出栈时按照添加的逆序应用</li>
 *     <li>支持多种函数式接口:Runnable、Consumer、Supplier、Function</li>
 *     <li>通过装饰器模式将熔断器和重试逻辑织入目标方法</li>
 * </ul>
 * 
 * <p>使用示例:
 * <pre>
 * {@code
 * // 创建管理器并添加熔断器和重试
 * R4jManager manager = new R4jManager()
 *     .acquire(circuitBreaker)
 *     .acquire(retry);
 * 
 * // 执行无参数的任务
 * manager.run(() -> System.out.println("执行任务"));
 * 
 * // 执行带参数的任务
 * manager.run("param", param -> process(param));
 * 
 * // 执行有返回值的任务
 * String result = manager.run(() -> fetchData());
 * }
 * </pre>
 * 
 * <p>注意事项:
 * <ul>
 *     <li>组件添加顺序会影响执行顺序(后添加的先执行)</li>
 *     <li>每次 run 方法调用会清空栈中的所有组件</li>
 *     <li>不支持同时添加相同类型的多个实例</li>
 * </ul>
 * 
 * @author LS
 * @see io.github.resilience4j.circuitbreaker.CircuitBreaker
 * @see io.github.resilience4j.retry.Retry
 */
public class R4jManager {
    
    /** 存储已获取的组件(CircuitBreaker 或 Retry) */
    private final Stack<Object> acquirement;

    /**
     * 默认构造函数
     * 
     * <p>初始化空的组件栈,等待通过 acquire 方法添加组件。
     */
    public R4jManager() {
        acquirement = new Stack<>();
    }

    /**
     * 添加熔断器组件
     * 
     * <p>将指定的 CircuitBreaker 添加到组件栈中,支持链式调用。
     * 
     * @param circuitBreaker 要添加的熔断器实例
     * @return 当前 R4jManager 实例,支持链式调用
     * 
     * @see io.github.resilience4j.circuitbreaker.CircuitBreaker
     */
    public R4jManager acquire(CircuitBreaker circuitBreaker) {
        if (circuitBreaker == null) {
            throw new IllegalArgumentException("CircuitBreaker instance cannot be null. Please check if it is correctly configured or retrieved.");
        }
        acquirement.add(circuitBreaker);
        return this;
    }

    /**
     * 添加重试组件
     * 
     * <p>将指定的 Retry 添加到组件栈中,支持链式调用。
     * 
     * @param retry 要添加的重试实例
     * @return 当前 R4jManager 实例,支持链式调用
     * 
     * @see io.github.resilience4j.retry.Retry
     */
    public R4jManager acquire(Retry retry) {
        if (retry == null) {
            throw new IllegalArgumentException("Retry instance cannot be null. Please check if it is correctly configured or retrieved.");
        }
        acquirement.push(retry);
        return this;
    }

    /**
     * 执行无参数的 Runnable 任务
     * 
     * <p>从组件栈中依次取出所有组件,将其装饰逻辑应用到目标 Runnable 上,
     * 最终执行装饰后的任务。组件出栈顺序决定了装饰的应用顺序。
     * 
     * <p>执行流程:
     * <ol>
     *     <li>从栈中弹出组件</li>
     *     <li>如果是 CircuitBreaker,则应用熔断器装饰器</li>
     *     <li>如果是 Retry,则应用重试装饰器</li>
     *     <li>重复直到栈为空</li>
     *     <li>执行最终装饰后的 Runnable</li>
     * </ol>
     * 
     * @param runnable 要执行的目标任务
     * 
     * <p>注意事项:
     * <ul>
     *     <li>此方法会清空组件栈,调用后需要重新添加组件</li>
     *     <li>如果栈中存在非 CircuitBreaker 或 Retry 类型的对象,将抛出 IllegalArgumentException</li>
     * </ul>
     */
    public void run(Runnable runnable) {
        Runnable cur = runnable;
        while (acquirement.size() != 0) {
            Object o = acquirement.pop();
            if (o instanceof CircuitBreaker) {
                cur = ((CircuitBreaker) o).decorateRunnable(cur);
            } else if (o instanceof Retry) {
                cur = Retry.decorateRunnable((Retry) o, cur);
            } else {
                throw new IllegalArgumentException("Unknown component type in stack: " + (o == null ? "null" : o.getClass().getName()) + ". Only CircuitBreaker and Retry are supported.");
            }
        }
        cur.run();
    }

    /**
     * 执行带参数的 Consumer 任务
     * 
     * <p>类似于 run(Runnable),但支持传入参数和 Consumer 处理逻辑。
     * 所有组件的装饰逻辑会应用到 Consumer 上,最后执行 consumer.accept(param)。
     * 
     * <p>执行流程:
     * <ol>
     *     <li>从栈中弹出组件</li>
     *     <li>如果是 CircuitBreaker,则应用熔断器装饰器</li>
     *     <li>如果是 Retry,则应用自定义的重试装饰器(支持异常重试)</li>
     *     <li>重复直到栈为空</li>
     *     <li>执行 consumer.accept(param)</li>
     * </ol>
     * 
     * @param <T> 参数类型
     * @param param 要传递给 Consumer 的参数
     * @param consumer 要执行的目标消费者函数
     * 
     * <p>注意事项:
     * <ul>
     *     <li>此方法会清空组件栈,调用后需要重新添加组件</li>
     *     <li>Retry 装饰器会捕获 RuntimeException 和普通 Exception 进行重试</li>
     * </ul>
     */
    public <T> void run(T param, Consumer<T> consumer) {
        Consumer<T> cur = consumer;
        while (acquirement.size() != 0) {
            Object o = acquirement.pop();
            if (o instanceof CircuitBreaker) {
                cur = ((CircuitBreaker) o).decorateConsumer(cur);
            } else if (o instanceof Retry) {
                cur = decorateConsumer((Retry) o, cur);
            } else {
                throw new IllegalArgumentException();
            }
        }
        consumer.accept(param);
    }

    /**
     * 执行有返回值的 Supplier 任务
     * 
     * <p>执行无参数但返回结果的任务,所有组件的装饰逻辑会应用到 Supplier 上。
     * 
     * <p>执行流程:
     * <ol>
     *     <li>从栈中弹出组件</li>
     *     <li>如果是 CircuitBreaker,则应用熔断器装饰器</li>
     *     <li>如果是 Retry,则应用重试装饰器</li>
     *     <li>重复直到栈为空</li>
     *     <li>执行 cur.get() 并返回结果</li>
     * </ol>
     * 
     * @param <T> 返回值类型
     * @param supplier 要执行的目标供应者函数
     * @return Supplier 执行后的结果
     * 
     * <p>注意事项:
     * <ul>
     *     <li>此方法会清空组件栈,调用后需要重新添加组件</li>
     * </ul>
     */
    public <T> T run(Supplier<T> supplier) {
        Supplier<T> cur = supplier;
        while (acquirement.size() != 0) {
            Object o = acquirement.pop();
            if (o instanceof CircuitBreaker) {
                cur = ((CircuitBreaker) o).decorateSupplier(cur);
            } else if (o instanceof Retry) {
                cur = Retry.decorateSupplier((Retry) o, cur);
            } else {
                throw new IllegalArgumentException();
            }
        }
        return cur.get();
    }

    /**
     * 执行带参数且有返回值的 Function 任务
     * 
     * <p>执行带输入参数并返回结果的任务,所有组件的装饰逻辑会应用到 Function 上。
     * 
     * <p>执行流程:
     * <ol>
     *     <li>从栈中弹出组件</li>
     *     <li>如果是 CircuitBreaker,则应用自定义的熔断器装饰器(记录执行时间)</li>
     *     <li>如果是 Retry,则应用重试装饰器</li>
     *     <li>重复直到栈为空</li>
     *     <li>执行 cur.apply(param) 并返回结果</li>
     * </ol>
     * 
     * @param <I> 输入参数类型
     * @param <O> 返回值类型
     * @param param 要传递给 Function 的参数
     * @param func 要执行的目标函数
     * @return Function 执行后的结果
     * 
     * <p>注意事项:
     * <ul>
     *     <li>此方法会清空组件栈,调用后需要重新添加组件</li>
     *     <li>CircuitBreaker 装饰器会记录方法执行时长并通知熔断器</li>
     * </ul>
     */
    public <I, O> O run(I param, Function<I, O> func) {
        Function<I, O> cur = func;
        while (acquirement.size() != 0) {
            Object o = acquirement.pop();
            if (o instanceof CircuitBreaker) {
                cur = decorateFunction((CircuitBreaker) o, cur);
            } else if (o instanceof Retry) {
                cur = Retry.decorateFunction((Retry) o, cur);
            } else {
                throw new IllegalArgumentException();
            }
        }
        return cur.apply(param);
    }

    /**
     * 装饰 Consumer 以支持 Retry 重试逻辑
     * 
     * <p>该方法返回一个新的 Consumer,在原始 Consumer 的基础上添加了重试机制。
     * 当执行过程中发生异常时,会根据 Retry 配置进行重试。
     * 
     * <p>重试逻辑:
     * <ol>
     *     <li>创建 Retry.Context 上下文对象</li>
     *     <li>在无限循环中尝试执行 consumer.accept(param)</li>
     *     <li>如果成功,调用 context.onComplete() 并返回</li>
     *     <li>如果发生 RuntimeException,调用 context.onRuntimeError(e) 决定是否重试</li>
     *     <li>如果发生普通 Exception,调用 context.onError(e) 决定是否重试</li>
     * </ol>
     * 
     * @param <T> 参数类型
     * @param retry 重试实例,提供重试策略和上下文
     * @param consumer 原始消费者函数
     * @return 装饰后的 Consumer,具备重试能力
     * 
     * <p>注意事项:
     * <ul>
     *     <li>使用 @SneakyThrows 注解处理受检异常,避免方法签名声明 throws</li>
     *     <li>重试策略由 Retry 实例的配置决定(如重试次数、延迟策略等)</li>
     * </ul>
     */
    private static <T> Consumer<T> decorateConsumer(Retry retry, Consumer<T> consumer) {
        return param -> new Consumer<T>() {
            @SneakyThrows
            @Override
            public void accept(T t) {
                Retry.Context<T> context = retry.context();

                while (true) {
                    try {
                        consumer.accept(param);
                        context.onComplete();
                        return;
                    } catch (RuntimeException e) {
                        context.onRuntimeError(e);
                    } catch (Exception e) {
                        context.onError(e);
                    }
                }
            }
        };
    }

    /**
     * 装饰 Function 以支持 CircuitBreaker 熔断逻辑
     * 
     * <p>该方法返回一个新的 Function,在原始 Function 的基础上添加了熔断器监控。
     * 熔断器会记录方法执行时长和结果,用于判断是否触发熔断。
     * 
     * <p>熔断逻辑:
     * <ol>
     *     <li>调用 circuitBreaker.acquirePermission() 获取执行权限</li>
     *     <li>记录开始时间戳</li>
     *     <li>执行 func.apply(param) 获取结果</li>
     *     <li>计算执行时长并调用 circuitBreaker.onResult() 通知结果</li>
     *     <li>如果发生异常,调用 circuitBreaker.onError() 通知错误</li>
     * </ol>
     * 
     * @param <I> 输入参数类型
     * @param <O> 返回值类型
     * @param circuitBreaker 熔断器实例,提供熔断策略和状态管理
     * @param func 原始函数
     * @return 装饰后的 Function,具备熔断监控能力
     * 
     * <p>注意事项:
     * <ul>
     *     <li>如果熔断器处于打开状态,acquirePermission() 可能抛出异常阻止执行</li>
     *     <li>执行时长用于熔断器的慢调用检测</li>
     * </ul>
     */
    private static <I, O> Function<I, O> decorateFunction(
            CircuitBreaker circuitBreaker,
            Function<I, O> func
    ) {
        return param -> {
            circuitBreaker.acquirePermission();
            long start = circuitBreaker.getCurrentTimestamp();

            long duration;
            try {
                O result = func.apply(param);
                duration = circuitBreaker.getCurrentTimestamp() - start;
                circuitBreaker.onResult(duration, circuitBreaker.getTimestampUnit(), result);
                return result;
            } catch (Exception e) {
                duration = circuitBreaker.getCurrentTimestamp() - start;
                circuitBreaker.onError(duration, circuitBreaker.getTimestampUnit(), e);
                throw e;
            }
        };
    }
}

场景演示

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 使用 R4jManager 和 R4jRegistry 实现重试机制的示例
 * 
 * <p>演示如何通过 R4jRegistry 获取组件实例,
 * 再通过 R4jManager 统一管理并执行任务。
 * 
 * <p>两种方式对比:
 * <ul>
 *     <li>方式 1(场景 1-5):直接创建组件实例(推荐,简单直接)</li>
 *     <li>方式 2(场景 6):通过 R4jRegistry 从 Spring 容器获取(适合企业级项目)</li>
 * </ul>
 * 
 * <p>核心优势:
 * <ul>
 *     <li>链式调用:acquire() 方法支持流畅的 API 设计</li>
 *     <li>统一接口:R4jManager 提供多种 run() 重载方法</li>
 *     <li>组件复用:可轻松组合 CircuitBreaker 和 Retry</li>
 * </ul>
 * 
 * @author LS
 * @see R4jManager
 * @see R4jRegistry
 * @see RetryExample
 */
public class R4jManagerRetryExample {

    /** 记录调用次数 */
    private static final AtomicInteger callCounter = new AtomicInteger(0);

    public static void main(String[] args) {
        System.out.println("========== 使用 R4jManager 实现重试机制 ==========\n");

        // 1. 创建自定义重试配置
        RetryConfig retryConfig = RetryConfig.<String>custom()
                .maxAttempts(5)                                    // 最多尝试 5 次
                .waitDuration(Duration.ofSeconds(1))               // 每次间隔 1 秒
                .retryOnException(e -> e instanceof RuntimeException)  // 仅重试运行时异常
                .ignoreExceptions(IllegalArgumentException.class)  // 忽略非法参数异常
                .build();

        // 2. 创建 Retry 实例
        Retry retry = Retry.of("networkCall", retryConfig);

        // 3. 演示场景 1:使用 R4jManager 执行 Supplier(有返回值)
        System.out.println("【场景 1】模拟网络抖动(第 3 次成功)");
        System.out.println("使用 R4jManager.run(Supplier) 方法");
        callCounter.set(0);
        
        try {
            String result = new R4jManager()
                    .acquire(retry)
                    .run(() -> simulateUnstableNetwork());
            
            System.out.println("最终结果: " + result);
            System.out.println("总调用次数: " + callCounter.get());
        } catch (Exception e) {
            System.out.println("重试耗尽,最终失败: " + e.getMessage());
            System.out.println("总调用次数: " + callCounter.get());
        }

        System.out.println();
        printSeparator();
        System.out.println();

        // 4. 演示场景 2:使用 R4jManager 执行 Function(带参数有返回值)
        System.out.println("【场景 2】模拟持续故障(带参数调用)");
        System.out.println("使用 R4jManager.run(param, Function) 方法");
        callCounter.set(0);
        
        String userId = "user123";
        try {
            String result = new R4jManager()
                    .acquire(retry)
                    .run(userId, (java.util.function.Function<String, String>) id -> simulatePersistentFailureWithParam(id));
            
            System.out.println("最终结果: " + result);
        } catch (Exception e) {
            System.out.println("重试耗尽,最终失败: " + e.getMessage());
            System.out.println("总调用次数: " + callCounter.get());
        }

        System.out.println();
        printSeparator();
        System.out.println();

        // 5. 演示场景 3:使用 R4jManager 执行 Consumer(带参数无返回值)
        System.out.println("【场景 3】模拟非法参数异常(不重试)");
        System.out.println("使用 R4jManager.run(param, Consumer) 方法");
        callCounter.set(0);
        
        try {
            new R4jManager()
                    .acquire(retry)
                    .run("testParam", (java.util.function.Consumer<String>) param -> simulateIllegalArgumentWithParam(param));
            
            System.out.println("执行成功");
        } catch (Exception e) {
            System.out.println("异常类型: " + e.getClass().getSimpleName());
            System.out.println("异常信息: " + e.getMessage());
            System.out.println("调用次数: " + callCounter.get() + " (被忽略的异常不会重试)");
        }

        System.out.println();
        printSeparator();
        System.out.println();

        // 6. 演示场景 4:使用 R4jManager 执行 Runnable(无参数无返回值)
        System.out.println("【场景 4】模拟网络抖动(无返回值)");
        System.out.println("使用 R4jManager.run(Runnable) 方法");
        callCounter.set(0);
        
        try {
            new R4jManager()
                    .acquire(retry)
                    .run(() -> {
                        simulateUnstableNetwork();
                        System.out.println("    🎉 任务执行完成!");
                    });
            
            System.out.println("总调用次数: " + callCounter.get());
        } catch (Exception e) {
            System.out.println("重试耗尽,最终失败: " + e.getMessage());
        }

        System.out.println();
        printSeparator();
        System.out.println();

        // 7. 演示场景 5:组合使用 CircuitBreaker + Retry
        System.out.println("【场景 5】组合使用熔断器 + 重试");
        System.out.println("先重试(应对临时故障),后熔断(防止雪崩)");
        
        // 创建默认熔断器
        io.github.resilience4j.circuitbreaker.CircuitBreaker circuitBreaker = 
                io.github.resilience4j.circuitbreaker.CircuitBreaker.ofDefaults("myCircuitBreaker");
        
        callCounter.set(0);
        
        try {
            String result = new R4jManager()
                    .acquire(circuitBreaker)  // 后添加,先执行(外层)
                    .acquire(retry)           // 先添加,后执行(内层)
                    .run(() -> simulateUnstableNetwork());
            
            System.out.println("最终结果: " + result);
            System.out.println("总调用次数: " + callCounter.get());
            System.out.println("熔断器状态: " + circuitBreaker.getState());
        } catch (Exception e) {
            System.out.println("失败: " + e.getMessage());
            System.out.println("熔断器状态: " + circuitBreaker.getState());
        }

        System.out.println();
        printSeparator();
        System.out.println();

        // 8. 演示场景 6:使用 R4jRegistry 获取组件实例(企业级方式)
        System.out.println("【场景 6】使用 R4jRegistry 获取组件实例");
        System.out.println("说明:通过 R4jRegistry 从 Spring 容器获取已配置的组件\n");
        
        try {
            // 方式 A:通过 R4jRegistry 从 Spring 容器获取(当前已启用)
            Retry retryFromRegistry = R4jRegistry.retry("registryRetry");
            CircuitBreaker cbFromRegistry = R4jRegistry.circuitBreaker("myCircuitBreaker");
            
            if (retryFromRegistry == null || cbFromRegistry == null) {
                throw new RuntimeException("Failed to retrieve components from R4jRegistry. Check applicationContext.xml.");
            }
            
            System.out.println("通过 R4jRegistry 获取组件成功!");
            System.out.println("使用 R4jManager 执行任务:");
            callCounter.set(0);
            
            String result = new R4jManager()
                    .acquire(cbFromRegistry)
                    .acquire(retryFromRegistry)
                    .run(() -> simulateUnstableNetwork());
            
            System.out.println("最终结果: " + result);
            System.out.println("总调用次数: " + callCounter.get());
            
        } catch (Exception e) {
            System.out.println("异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 模拟不稳定的网络连接(前 2 次失败,第 3 次成功)
     */
    private static String simulateUnstableNetwork() {
        int attempt = callCounter.incrementAndGet();
        System.out.println("  第 " + attempt + " 次调用...");
        
        if (attempt < 3) {
            System.out.println("    ❌ 网络超时,抛出异常");
            throw new RuntimeException("Connection timeout");
        }
        
        System.out.println("    ✅ 连接成功");
        return "Success from attempt " + attempt;
    }

    /**
     * 模拟持续故障(带参数版本)
     */
    private static String simulatePersistentFailureWithParam(String userId) {
        int attempt = callCounter.incrementAndGet();
        System.out.println("  第 " + attempt + " 次调用,用户: " + userId);
        
        System.out.println("    ❌ 服务不可用");
        throw new RuntimeException("Service unavailable for user: " + userId);
    }

    /**
     * 模拟非法参数异常(带参数版本)
     */
    private static void simulateIllegalArgumentWithParam(String param) {
        int attempt = callCounter.incrementAndGet();
        System.out.println("  第 " + attempt + " 次调用,参数: " + param);
        
        System.out.println("    ❌ 参数错误");
        throw new IllegalArgumentException("Invalid parameter: " + param);
    }

    /**
     * 打印分隔线(兼容 Java 8)
     */
    private static void printSeparator() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 50; i++) {
            sb.append('=');
        }
        System.out.println(sb.toString());
    }
}

运行结果

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐