【源码】openfeign集成spring-cloud-starter-circuitbreaker-resilience4j 源码分析
依赖
本文源码分析pom依赖如下,
spring cloud 版本 2023.0.0
<dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
circuitbreaker 版本 3.1.0
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
<version>3.1.0</version>
</dependency>
自动装配入口
org.springframework.cloud.openfeign.FeignAutoConfiguration
.CircuitBreakerPresentFeignTargeterConfiguration.circuitBreakerFeignTargeter
详细配置代码:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CircuitBreaker.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.circuitbreaker.enabled", havingValue = "true")
protected static class CircuitBreakerPresentFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public Targeter defaultFeignTargeter() {
return new DefaultTargeter();
}
// @A0
@Bean
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled",
havingValue = "false")
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
return new DefaultCircuitBreakerNameResolver();
}
@Bean
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled",
havingValue = "true", matchIfMissing = true)
public CircuitBreakerNameResolver alphanumericCircuitBreakerNameResolver() {
return new AlphanumericCircuitBreakerNameResolver();
}
// @A1
@SuppressWarnings("rawtypes")
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CircuitBreakerFactory.class)
public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory,
@Value("${spring.cloud.openfeign.circuitbreaker.group.enabled:false}") boolean circuitBreakerGroupEnabled, CircuitBreakerNameResolver circuitBreakerNameResolver) {
return new FeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerGroupEnabled,
circuitBreakerNameResolver);
}
static class DefaultCircuitBreakerNameResolver implements CircuitBreakerNameResolver {
@Override
public String resolveCircuitBreakerName(String feignClientName, Target<?> target, Method method) {
return Feign.configKey(target.type(), method);
}
}
static class AlphanumericCircuitBreakerNameResolver extends DefaultCircuitBreakerNameResolver {
@Override
public String resolveCircuitBreakerName(String feignClientName, Target<?> target, Method method) {
return super.resolveCircuitBreakerName(feignClientName, target, method).replaceAll("[^a-zA-Z0-9]", "");
}
}
}
下面介绍一下这些核心类
Targeter类
circuitBreakerFeignTargeter 方法会返回一个 @A1:Targeter 对象 ,依赖的参数是CircuitBreakerFactory circuitBreakerFactory,这个CircuitBreakerFactory 参数是在 org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JAutoConfiguration中定义的,配置如下:
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public Resilience4JCircuitBreakerFactory resilience4jCircuitBreakerFactory(
CircuitBreakerRegistry circuitBreakerRegistry, TimeLimiterRegistry timeLimiterRegistry,
@Autowired(required = false) Resilience4jBulkheadProvider bulkheadProvider,
Resilience4JConfigurationProperties resilience4JConfigurationProperties) {
Resilience4JCircuitBreakerFactory factory = new Resilience4JCircuitBreakerFactory(circuitBreakerRegistry,
timeLimiterRegistry, bulkheadProvider, resilience4JConfigurationProperties);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}
上文提到的 @A1:Targeter 对象 是一个FeignCircuitBreakerTargeter实例,这个类主要作用是在org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject起作用,创建OpenFeign代理对象,最终执行的代码是:feign.ReflectiveFeign#newInstance(feign.Target, C)
public <T> T newInstance(Target<T> target, C requestContext) {
ReflectiveFeign.TargetSpecificationVerifier.verify(target);
Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = this.targetToHandlersByName.apply(target, requestContext);
// @B
InvocationHandler handler = this.factory.create(target, methodToHandler);
T proxy = (T)Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
for(InvocationHandlerFactory.MethodHandler methodHandler : methodToHandler.values()) {
if (methodHandler instanceof DefaultMethodHandler) {
((DefaultMethodHandler)methodHandler).bindTo(proxy);
}
}
return proxy;
}
@B 位置的InvocationHandler handler 真实类型是FeignCircuitBreakerInvocationHandler
从这个方法,就能看出openfeign的乾坤了;说实话,这细节太多 记也记不住;
CircuitBreakerNameResolver 类
自动装配CircuitBreakerPresentFeignTargeterConfiguration定义了一个CircuitBreakerNameResolver类 // @A0 ,这个类的核心作用是定义哪些类、方法能共用一个断路器;
框架默认注入的是DefaultCircuitBreakerNameResolver,在没有开启circuitBreakerGroupEnabled的情况下,这个类会把一个方法定义使用一个断路器;
如果开启circuitBreakerGroupEnabled,在代码执行的时候会首先找方法的配置,方法没有配置则找feignClientName对应的配置;
如果我们想让同一个@FeignClient 标记的类使用同一个CircuitBreaker,就可以向容器注入一个自定义CircuitBreakerNameResolver并实现方法即可;
@Configuration
public class Resilience4JFeignConfig {
/**
* 框架默认把方法作为断路器名称
* 这里自定义把 FeignClient 作为断路器名称
*/
@Bean
public CircuitBreakerNameResolver feignClientGroupResolver() {
//
return (feignClientName, target, method) -> feignClientName;
}
}
使用CircuitBreakerNameResolver解析断路器名字的代码在:
org.springframework.cloud.openfeign.FeignCircuitBreakerInvocationHandler#invoke
有兴趣可以看看

断路器调用入口
feign调用CircuitBreaker栈:
调用栈截图:1、代理类被调用; 2、java.lang.reflect.InvocationHandler子类FeignCircuitBreakerInvocationHandler执行
FeignCircuitBreakerInvocationHandler后续调用栈:
org.springframework.cloud.openfeign.FeignCircuitBreakerInvocationHandler.invoke → org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory.create
↓
org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreaker.run
↓
org.springframework.cloud.circuitbreaker.resilience4j.Resilience4jBulkheadProvider.run
如果要细细研究,就只能打断点跟踪了;
resilience4j核心模块
| 核心模块 | 核心作用 | 一句话场景 |
|---|---|---|
| Circuit Breaker (熔断器) | 当服务故障率或慢请求率超过阈值时,自动“熔断”请求,防止故障像雪球一样越滚越大 | 下游数据库连接超时率达50%,直接熔断快速失败,防止应用线程池被耗尽。 |
| Rate Limiter (限流器) | 基于令牌桶算法,限制单位时间内的请求数量,保护服务不被突发流量冲垮 | 短信验证码接口每秒只允许处理10个请求,超出部分直接拒绝。 |
| Retry (重试) | 对因网络抖动等瞬时故障失败的操作,按照指数退避等策略自动重试 | 调用第三方API偶发超时,自动重试2次,大概率能成功。 |
| Bulkhead (隔离/舱壁) | 限制对某个服务的并发请求数,资源隔离。就算一个服务慢了,也不会耗尽整个应用的线程池 | 文件导出功能耗时较长,限制其最多10个线程并发,不影响其他接口 |
| Time Limiter (超时控制) | 为操作设置最大执行时间,超时则直接中断并抛出异常,避免线程无限阻塞 | 调用外部支付接口,超过3秒未响应就超时放弃,释放资源。 |
| Fallback (降级) | 当熔断、限流、超时等触发时,执行备用逻辑(如返回缓存数据或默认值) | 推荐服务挂了,返回一个默认的热门商品列表给用户。 |
接上面 Resilience4JCircuitBreakerFactory.create 方法,这个方法会根据当前配置,创建一个Resilience4JCircuitBreaker对象;从这里看出来每次请求都会创建一个Resilience4JCircuitBreaker对象,感觉流程上可以优化;
我们看看 org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreaker 的run方法
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
final Map<String, String> tags = Map.of(CIRCUIT_BREAKER_GROUP_TAG, this.groupName);
Optional<TimeLimiter> timeLimiter = loadTimeLimiter();
io.github.resilience4j.circuitbreaker.CircuitBreaker defaultCircuitBreaker = registry.circuitBreaker(this.id,
this.circuitBreakerConfig, tags);
circuitBreakerCustomizer.ifPresent(customizer -> customizer.customize(defaultCircuitBreaker));
if (bulkheadProvider != null) {
// @C
if (executorService != null) {
Supplier<Future<T>> futureSupplier = () -> executorService.submit(toRun::get);
/* conditionally wrap in time-limiter */
Callable<T> timeLimitedCall = timeLimiter
// @C1
.map(tl -> TimeLimiter.decorateFutureSupplier(tl, futureSupplier))
.orElse(() -> futureSupplier.get().get());
// @C2
Callable<T> bulkheadCall = bulkheadProvider.decorateCallable(this.groupName, tags, timeLimitedCall);
// @C3
Callable<T> circuitBreakerCall = io.github.resilience4j.circuitbreaker.CircuitBreaker
.decorateCallable(defaultCircuitBreaker, bulkheadCall);
return getAndApplyFallback(circuitBreakerCall, fallback);
}
else {
Callable<T> bulkheadCall = bulkheadProvider.decorateCallable(this.groupName, tags, toRun::get);
Callable<T> circuitBreakerCall = io.github.resilience4j.circuitbreaker.CircuitBreaker
.decorateCallable(defaultCircuitBreaker, bulkheadCall);
return getAndApplyFallback(circuitBreakerCall, fallback);
}
} else {
if (executorService != null) {
Supplier<Future<T>> futureSupplier = () -> executorService.submit(toRun::get);
/* conditionally wrap in time-limiter */
Callable<T> restrictedCall = timeLimiter
.map(tl -> TimeLimiter.decorateFutureSupplier(tl, futureSupplier))
.orElse(() -> futureSupplier.get().get());
Callable<T> callable = io.github.resilience4j.circuitbreaker.CircuitBreaker
.decorateCallable(defaultCircuitBreaker, restrictedCall);
return getAndApplyFallback(callable, fallback);
}
else {
Supplier<T> decorator = io.github.resilience4j.circuitbreaker.CircuitBreaker
.decorateSupplier(defaultCircuitBreaker, toRun);
return getAndApplyFallback(decorator, fallback);
}
}
}
这个方法解答了springcloud对resilience4j封装的核心包装逻辑:
我们分析一下上面代码 @C 最复杂的分支(其他分支都是当前最复杂分支的简化):
@C1: 使用TimeLimiter对FeignCircuitBreakerInvocationHandler的asSupplier逻辑进行装饰
@C2:使用Bulkhead对逻辑进行装饰
@C3:使用CircuitBreaker对逻辑进行装饰
最终执行顺序:
CircuitBreaker(校验断路器状态) → Bulkhead(可选线程池和信号量两种方式实现) → TimeLimiter → CircuitBreaker 线程池 → FeignCircuitBreakerInvocationHandler业务逻辑
这里有一个细节:TimeLimiter 必须基于线程池才能发挥作用,因为TimeLimiter核心的api是通过future.get判断超时,需要依赖新线程;
复杂的情况下涉及到两个线程池,加上tomcat容器线程池就是3个了,所以要精心设计配置,否则就会出现线程不合理影响性能,cpu调度线程资源消耗加重;
配置说明及示例
以下是cloud开启circuitbreaker配置和openfeign超时配置
spring:
cloud:
# openfeign配置
openfeign:
# @FeignClient的fallback 生效
circuitbreaker:
enabled: true
# openfeign超时设置
client:
config:
default:
# 日志级别
loggerLevel: FULL
# 请求超时时间(ms)
connectTimeout: 3000
# 响应超时时间(ms)
readTimeout: 2000
# 根据服务名字匹配的
xxx-server:
#
loggerLevel: FULL
# (ms)
connectTimeout: 3000
# (ms)
readTimeout: 4000
# 对应配置文件 Resilience4JConfigurationProperties
circuitbreaker:
resilience4j:
# 超时控制权完全交还给 Feign
disableTimeLimiter: true
# 关闭circuitbreaker线程池
disableThreadPool: true
熔断器规则配置:
# resilience4j circuitbreaker断路器配置。配合@CircuitBreaker注解使用
resilience4j.circuitbreaker:
configs:
#熔断机制的默认设置
default:
#状态收集器类型
#COUNT_BASED:根据数量计算,slidingWindowSize为次数
#TIME_BASED:根据时间计算,slidingWindowSize为秒数
slidingWindowType: TIME_BASED
# 时间窗口的大小为60秒
slidingWindowSize: 60
# 在单位时间窗口内调用失败率达到 n% 后会启动断路器
failureRateThreshold: 40
# 在单位时间窗口内最少需要多少次调用才能开始进行统计计算
minimumNumberOfCalls: 10
# 进入halfOpen状态时,可以被调用次数,就算这些请求的失败率,低于设置的失败率变为close状态,否则变为open。
permittedNumberOfCallsInHalfOpenState: 10
# 允许断路器自动由打开状态转换为半开状态
# 是否自动进入halfOpen状态,默认false-一定时间后进入halfOpen,ture-需要通过接口执行。
automaticTransitionFromOpenToHalfOpenEnabled: true
# 断路器打开状态转换为半开状态需要等待的时间
waitDurationInOpenState: 5s
# 消费数据处理的缓存数据量大小(如果事件产生速度大于消费速度,缓冲区可以临时存储事件,防止事件丢失)
eventConsumerBufferSize: 4000
# 哪些异常都当作失败来处理(会被fallback)
recordExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
- java.util.concurrent.TimeoutException
- java.lang.IllegalArgumentException
- feign.RetryableException
- java.net.SocketTimeoutException
- feign.FeignException.ServiceUnavailable
# 哪些异常直接忽略
ignoreExceptions:
- java.lang.IllegalStateException
# 自定义熔断器实例
instances:
# 如果有自定义短路逻辑,则可以单独配置。
xxx-server:
#基于默认断路器
baseConfig: default
over~~
更多推荐



所有评论(0)