SpringCloud断路器功能生效的条件是:容器中有CircuitBreakerFactory类型的bean,如果没有,则注入默认的DefaultTargeter;如果有,则注入FeignCurcuitBreakerTargeter。
class FeignAutoConfiguration{
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public Targeter defaultFeignTargeter() {
return new DefaultTargeter();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CircuitBreakerFactory.class)
public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory,
@Value("${feign.circuitbreaker.group.enabled:false}") boolean circuitBreakerGroupEnabled,
CircuitBreakerNameResolver circuitBreakerNameResolver) {
return new FeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerGroupEnabled,
circuitBreakerNameResolver);
}
}
FeignCircuitBreakerTargeter通过FeignCircuitBreaker.Builder创建Feign接口代理,拦截器器是FeignCircuitBreakerInvocationHandler,它通过CircuitBreakerFactory创建CircuitBreaker,然后执行CircuitBreaker.run执行断路器逻辑和业务逻辑
class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
...
CircuitBreaker circuitBreaker = circuitBreakerGroupEnabled ? factory.create(circuitName, feignClientName)
: factory.create(circuitName);
return circuitBreaker.run(supplier, fallbackFunction);
}
}
Resilience4jCircuitBreaker
是CircuitBreaker的Resilience4j实现类
- 执行业务逻辑之前,尝试获取准许,CircuitBreaker.acquirePermission()
- 执行业务逻辑之后,如果没有异常,执行CircuitBreaker.onResult;如果有异常,执行CircuitBreaker.onError,并把执行时间、结果或异常传入
//注意,这里的CircuitBreaker 是Resilience4j包(io.github.resilience4j.circuitbreaker.CircuitBreaker)下的断路器,
//而不是springcloud的断路器,org.springframework.cloud.client.circuitbreaker.CircuitBreaker
static <T> Supplier<T> decorateSupplier(CircuitBreaker circuitBreaker, Supplier<T> supplier) {
return () -> {
circuitBreaker.acquirePermission();
final long start = circuitBreaker.getCurrentTimestamp();
try {
T result = supplier.get();
long duration = circuitBreaker.getCurrentTimestamp() - start;
circuitBreaker.onResult(duration, circuitBreaker.getTimestampUnit(), result);
return result;
} catch (Exception exception) {
// Do not handle java.lang.Error
long duration = circuitBreaker.getCurrentTimestamp() - start;
circuitBreaker.onError(duration, circuitBreaker.getTimestampUnit(), exception);
throw exception;
}
};
}
CircuitBreakerStateMachine
是CircuitBreaker的实现类,断路器同一时刻只会处于一种状态,状态之间可以相互转换
状态:
- 关闭:所有调用都能执行,执行后根据结果是否异常和执行时间计算慢调用比例和失败比例,当调用的总次数达到一定值(默认100),并且其中一个比例达到阈值(慢调用100%,失败50%)时,断路器转变成开启状态
- 开启:所有调用都不能执行,抛CallNotPermittedException(默认Throwable.writableStackTrace=true,会打印异常栈),执行降级Fallback逻辑。开启后,等待一段时间自动变成半开状态,等待时间默认是1分钟,等待策略有固定等待策略、范围随机、指数增长
- 半开:用于测试调用的执行情况,允许通过少量调用请求(默认10),统计这些请求的慢调用比例和失败比例,当比例都小于阈值时,关闭;否则开启
- 禁用(disable):断路器不起作用
- 强制打开:所有调用都不能执行
- 只度量:所有调用都能执行,超出门限时触发事件,并且只触发一次
public final class CircuitBreakerStateMachine implements CircuitBreaker {
//断路器状态,状态切换通过原子类的方法实现,java.util.concurrent.atomic.AtomicReference#getAndUpdate
private final AtomicReference<CircuitBreakerState> stateReference;
@Override
public void acquirePermission() {
//调用前执行,执行状态的获取方法
stateReference.get().acquirePermission();
}
@Override
public void onSuccess(long duration, TimeUnit durationUnit) {
stateReference.get().onSuccess(duration, durationUnit);
}
@Override
public void onError(long duration, TimeUnit durationUnit, Throwable throwable) {
stateReference.get().onError(duration, durationUnit, throwable);
}
@Override
public void transitionToClosedState() {
//状态切换时,会重新创建一个新的状态
stateTransition(CLOSED, currentState -> new ClosedState());
}
}
状态的公共接口
private interface CircuitBreakerState {
boolean tryAcquirePermission();
void acquirePermission();
void releasePermission();
void onError(long duration, TimeUnit durationUnit, Throwable throwable);
void onSuccess(long duration, TimeUnit durationUnit);
//实际上是转变为开启状态的次数,用来计算开启到半开的等待时间
int attempts();
//负责统计调用次数和比例,每个状态对象对应一个
CircuitBreakerMetrics getMetrics();
}
FixedSizeSlidingWindowMetrics
固定请求数滑动窗口,统计最近n个请求的指标(慢调用次数,失败次数,总次数,总调用时间)。通过一个环形数组,数组每个元素记录了最近n个请求的指标,维护最近一次调用对应的索引,每次调用时,索引值向后移动一位,将总的指标减去这个元素的指标,重置移动后对应的元素的指标,再将最新的调用指标记录在这个元素上。
public class FixedSizeSlidingWindowMetrics implements Metrics {
//滑动窗口大小
private final int windowSize;
//最近n次调用的总指标
private final TotalAggregation totalAggregation;
//滑动窗口,环形数组
private final Measurement[] measurements;
//最近一次请求的滑动窗口位置
int headIndex;
@Override
public synchronized Snapshot record(long duration, TimeUnit durationUnit, Outcome outcome) {
//在总指标中记录
totalAggregation.record(duration, durationUnit, outcome);
//在滑动窗口中记录,为了删除
moveWindowByOne().record(duration, durationUnit, outcome);
return new SnapshotImpl(totalAggregation);
}
private Measurement moveWindowByOne() {
moveHeadIndexByOne();
Measurement latestMeasurement = getLatestMeasurement();
//删除过期的指标
totalAggregation.removeBucket(latestMeasurement);
//重置为0
latestMeasurement.reset();
return latestMeasurement;
}
}
SlidingTimeWindowMetrics
滑动时间窗口,记录的是最近n秒的指标。和FixedSizeSlidingWindowMetrics 类似,也是通过环形数组实现,但数组的每个元素代表的是1秒内的指标,可能对应多次调用,也可能0次。每个元素对应一个秒级的时间戳,移动一格代表时间+1s,当有新的请求时,移动滑动窗口的结束位置直到请求的时间戳,从总指标中删除移动过的元素的指标,重置指标,重设元素的时间戳
private PartialAggregation moveWindowToCurrentEpochSecond(
PartialAggregation latestPartialAggregation) {
long currentEpochSecond = clock.instant().getEpochSecond();
//最近的位置表示滑动窗口时间的最大值,如果当前时刻大于它,则需要移动
long differenceInSeconds = currentEpochSecond - latestPartialAggregation.getEpochSecond();
if (differenceInSeconds == 0) {
return latestPartialAggregation;
}
//整个滑动窗口都过期了
long secondsToMoveTheWindow = Math.min(differenceInSeconds, timeWindowSizeInSeconds);
PartialAggregation currentPartialAggregation;
do {
secondsToMoveTheWindow--;
//移动滑动窗口的结束位置
moveHeadIndexByOne();
currentPartialAggregation = getLatestPartialAggregation();
//结束位置之后的指标已经过期了
totalAggregation.removeBucket(currentPartialAggregation);
currentPartialAggregation.reset(currentEpochSecond - secondsToMoveTheWindow);
} while (secondsToMoveTheWindow > 0);
return currentPartialAggregation;
}
滑动时间窗口的时间复杂度是O(n),因为最坏情况下整个窗口都需要重置;固定调用次数滑动窗口的时间复杂度是O(1),是默认的实现,因为每次调用只会移动一次窗口