聊聊resilience4j的CircuitBreakerConfig

codecraft

本文主要研究一下resilience4j的CircuitBreakerConfig

CircuitBreakerConfig

resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java

/**
 * A {@link CircuitBreakerConfig} configures a {@link CircuitBreaker}
 */
public class CircuitBreakerConfig {

    public static final int DEFAULT_MAX_FAILURE_THRESHOLD = 50; // Percentage
    public static final int DEFAULT_WAIT_DURATION_IN_OPEN_STATE = 60; // Seconds
    public static final int DEFAULT_RING_BUFFER_SIZE_IN_HALF_OPEN_STATE = 10;
    public static final int DEFAULT_RING_BUFFER_SIZE_IN_CLOSED_STATE = 100;
    public static final Predicate<Throwable> DEFAULT_RECORD_FAILURE_PREDICATE = (throwable) -> true;

    private float failureRateThreshold = DEFAULT_MAX_FAILURE_THRESHOLD;
    private int ringBufferSizeInHalfOpenState = DEFAULT_RING_BUFFER_SIZE_IN_HALF_OPEN_STATE;
    private int ringBufferSizeInClosedState = DEFAULT_RING_BUFFER_SIZE_IN_CLOSED_STATE;
    private Duration waitDurationInOpenState = Duration.ofSeconds(DEFAULT_WAIT_DURATION_IN_OPEN_STATE);
    // The default exception predicate counts all exceptions as failures.
    private Predicate<Throwable> recordFailurePredicate = DEFAULT_RECORD_FAILURE_PREDICATE;
    private boolean automaticTransitionFromOpenToHalfOpenEnabled = false;

    //......
}
这里涉及了几个参数如下:
  • failureRateThreshold,默认为50,即失败率阈值为50%
  • ringBufferSizeInHalfOpenState,设置当断路器处于HALF_OPEN状态下的ring buffer的大小,它存储了最近一段时间请求的成功失败状态,默认为10
  • ringBufferSizeInClosedState,设置当断路器处于CLOSED状态下的ring buffer的大小,它存储了最近一段时间请求的成功失败状态,默认为100
  • waitDurationInOpenState,用来指定断路器从OPEN到HALF_OPEN状态等待的时长,默认是60秒
  • recordFailurePredicate,用于判断哪些异常应该算作失败纳入断路器统计,默认是Throwable类型
  • automaticTransitionFromOpenToHalfOpenEnabled,当waitDurationInOpenState时间一过,是否自动从OPEN切换到HALF_OPEN,默认为true

CircuitBreakerMetrics

resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerMetrics.java

class CircuitBreakerMetrics implements CircuitBreaker.Metrics {

    private final int ringBufferSize;
    private final RingBitSet ringBitSet;
    private final LongAdder numberOfNotPermittedCalls;

    CircuitBreakerMetrics(int ringBufferSize) {
        this(ringBufferSize, null);
    }

    CircuitBreakerMetrics(int ringBufferSize, RingBitSet sourceSet) {
        this.ringBufferSize = ringBufferSize;
        if(sourceSet != null) {
            this.ringBitSet = new RingBitSet(this.ringBufferSize, sourceSet);
        }else{
            this.ringBitSet = new RingBitSet(this.ringBufferSize);
        }
        this.numberOfNotPermittedCalls = new LongAdder();
    }

    /**
     * Creates a new CircuitBreakerMetrics instance and copies the content of the current RingBitSet
     * into the new RingBitSet.
     *
     * @param targetRingBufferSize the ringBufferSize of the new CircuitBreakerMetrics instances
     * @return a CircuitBreakerMetrics
     */
    public CircuitBreakerMetrics copy(int targetRingBufferSize) {
        return new CircuitBreakerMetrics(targetRingBufferSize, this.ringBitSet);
    }

    /**
     * Records a failed call and returns the current failure rate in percentage.
     *
     * @return the current failure rate  in percentage.
     */
    float onError() {
        int currentNumberOfFailedCalls = ringBitSet.setNextBit(true);
        return getFailureRate(currentNumberOfFailedCalls);
    }

    /**
     * Records a successful call and returns the current failure rate in percentage.
     *
     * @return the current failure rate in percentage.
     */
    float onSuccess() {
        int currentNumberOfFailedCalls = ringBitSet.setNextBit(false);
        return getFailureRate(currentNumberOfFailedCalls);
    }

    /**
     * Records a call which was not permitted, because the CircuitBreaker state is OPEN.
     */
    void onCallNotPermitted() {
        numberOfNotPermittedCalls.increment();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public float getFailureRate() {
        return getFailureRate(getNumberOfFailedCalls());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getMaxNumberOfBufferedCalls() {
        return ringBufferSize;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getNumberOfSuccessfulCalls() {
        return getNumberOfBufferedCalls() - getNumberOfFailedCalls();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getNumberOfBufferedCalls() {
        return this.ringBitSet.length();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getNumberOfNotPermittedCalls() {
        return this.numberOfNotPermittedCalls.sum();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getNumberOfFailedCalls() {
        return this.ringBitSet.cardinality();
    }

    private float getFailureRate(int numberOfFailedCalls) {
        if (getNumberOfBufferedCalls() < ringBufferSize) {
            return -1.0f;
        }
        return numberOfFailedCalls * 100.0f / ringBufferSize;
    }
}
  • ringBitSet.setNextBit方法返回的是这个ringBitSet中被设置为true的个数,这里就是numberOfFailedCalls
  • getFailureRate里头先判断,如果ringBitSet中的个数没有达到ringBufferSize指定的大小,则返回-1.0f,则不计算失败率
  • 失败率的计算就是numberOfFailedCalls * 100.0f / ringBufferSize

RingBitSet

resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/internal/RingBitSet.java

/**
 * A ring bit set which stores bits up to a maximum size of bits.
 */
class RingBitSet {

    private final int size;
    private final BitSetMod bitSet;

    private boolean notFull;
    private int index = -1;

    private volatile int length;
    private volatile int cardinality = 0;

    //......

    /**
     * Sets the bit at the next index to the specified value.
     *
     * @param value a boolean value to set
     * @return the number of bits set to {@code true}
     */
    public synchronized int setNextBit(boolean value) {
        increaseLength();
        index = (index + 1) % size;

        int previous = bitSet.set(index, value);
        int current = value ? 1 : 0;
        cardinality = cardinality - previous + current;
        return cardinality;
    }

    private void increaseLength() {
        if (notFull) {
            int nextLength = length + 1;
            if (nextLength < size) {
                length = nextLength;
            } else {
                length = size;
                notFull = false;
            }
        }
    }
}
  • 重点看setNextBit方法,这里的cardinality就是ringbuffer中设置为true的个数
  • increaseLength方法,如果length不超过size则递增,超过则重置为size大小,即最大为size大小,然后用notFull标记是否满

HalfOpenState

resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/internal/HalfOpenState.java

final class HalfOpenState extends CircuitBreakerState {

    private CircuitBreakerMetrics circuitBreakerMetrics;
    private final float failureRateThreshold;

    HalfOpenState(CircuitBreakerStateMachine stateMachine) {
        super(stateMachine);
        CircuitBreakerConfig circuitBreakerConfig = stateMachine.getCircuitBreakerConfig();
        this.circuitBreakerMetrics = new CircuitBreakerMetrics(
                circuitBreakerConfig.getRingBufferSizeInHalfOpenState());
        this.failureRateThreshold = stateMachine.getCircuitBreakerConfig().getFailureRateThreshold();
    }

    /**
     * Returns always true, because the CircuitBreaker is half open.
     *
     * @return always true, because the CircuitBreaker is half open.
     */
    @Override
    boolean isCallPermitted() {
        return true;
    }

    @Override
    void onError(Throwable throwable) {
        // CircuitBreakerMetrics is thread-safe
        checkFailureRate(circuitBreakerMetrics.onError());
    }

    @Override
    void onSuccess() {
        // CircuitBreakerMetrics is thread-safe
        checkFailureRate(circuitBreakerMetrics.onSuccess());
    }

    /**
     * Checks if the current failure rate is above or below the threshold.
     * If the failure rate is above the threshold, transition the state machine to OPEN state.
     * If the failure rate is below the threshold, transition the state machine to CLOSED state.
     *
     * @param currentFailureRate the current failure rate
     */
    private void checkFailureRate(float currentFailureRate) {
        if(currentFailureRate != -1){
            if(currentFailureRate >= failureRateThreshold) {
                stateMachine.transitionToOpenState();
            }else{
                stateMachine.transitionToClosedState();
            }
        }
    }

    /**
     * Get the state of the CircuitBreaker
     */
    @Override
    CircuitBreaker.State getState() {
        return CircuitBreaker.State.HALF_OPEN;
    }

    @Override
    CircuitBreakerMetrics getMetrics() {
        return circuitBreakerMetrics;
    }
}
  • 这里的ringBufferSize为getRingBufferSizeInHalfOpenState
  • 这里onError以及onSuccess都会触发checkFailureRate,检查失败率是否达到阈值
  • checkFailureRate判断的时候首先排除掉-1.0f,然后如果达到阈值,则将状态切换到OPEN,不达到阈值则切换到CLOSED状态

ClosedState

resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/internal/ClosedState.java

final class ClosedState extends CircuitBreakerState {

    private final CircuitBreakerMetrics circuitBreakerMetrics;
    private final float failureRateThreshold;

    ClosedState(CircuitBreakerStateMachine stateMachine) {
        this(stateMachine, null);
    }

    ClosedState(CircuitBreakerStateMachine stateMachine, CircuitBreakerMetrics circuitBreakerMetrics) {
        super(stateMachine);
        CircuitBreakerConfig circuitBreakerConfig = stateMachine.getCircuitBreakerConfig();
        if(circuitBreakerMetrics == null){
            this.circuitBreakerMetrics = new CircuitBreakerMetrics(
                circuitBreakerConfig.getRingBufferSizeInClosedState());
        }else{
            this.circuitBreakerMetrics = circuitBreakerMetrics.copy(circuitBreakerConfig.getRingBufferSizeInClosedState());
        }
        this.failureRateThreshold = stateMachine.getCircuitBreakerConfig().getFailureRateThreshold();
    }

    /**
     * Returns always true, because the CircuitBreaker is closed.
     *
     * @return always true, because the CircuitBreaker is closed.
     */
    @Override
    boolean isCallPermitted() {
        return true;
    }

    @Override
    void onError(Throwable throwable) {
        // CircuitBreakerMetrics is thread-safe
        checkFailureRate(circuitBreakerMetrics.onError());
    }

    @Override
    void onSuccess() {
        // CircuitBreakerMetrics is thread-safe
        checkFailureRate(circuitBreakerMetrics.onSuccess());
    }

    /**
     * Checks if the current failure rate is above the threshold.
     * If the failure rate is above the threshold, transitions the state machine to OPEN state.
     *
     * @param currentFailureRate the current failure rate
     */
    private void checkFailureRate(float currentFailureRate) {
        if (currentFailureRate >= failureRateThreshold) {
            // Transition the state machine to OPEN state, because the failure rate is above the threshold
            stateMachine.transitionToOpenState();
        }
    }

    /**
     * Get the state of the CircuitBreaker
     */
    @Override
    CircuitBreaker.State getState() {
        return CircuitBreaker.State.CLOSED;
    }
    /**
     *
     * Get metrics of the CircuitBreaker
     */
    @Override
    CircuitBreakerMetrics getMetrics() {
        return circuitBreakerMetrics;
    }
}
  • 这里的ringBufferSize为getRingBufferSizeInClosedState
  • 这里onError以及onSuccess都会触发checkFailureRate,检查失败率是否达到阈值
  • checkFailureRate判断如果大于设定的阈值,则把状态变为OPEN

小结

  • resilience4j的CircuitBreakerConfig主要设置了failureRateThreshold、ringBufferSizeInHalfOpenState、ringBufferSizeInClosedState、waitDurationInOpenState、recordFailurePredicate、automaticTransitionFromOpenToHalfOpenEnabled这几个配置。
  • ringBufferSizeInHalfOpenState、ringBufferSizeInClosedState设置的是在ringbuffer的大小,同时也影响failureRate的计算,即如果ringbuffer中的长度小于这个大小,则不会计算失败率。
  • ringbuffer中的length会递增,但是不会超过指定的大小,最大为指定的size

doc

阅读 1.6k

code-craft
spring boot , docker and so on 欢迎关注微信公众号: geek_luandun

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很...

11.6k 声望
1.9k 粉丝
0 条评论
你知道吗?

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很...

11.6k 声望
1.9k 粉丝
文章目录
宣传栏