文档章节

聊聊resilience4j的bulkhead

go4it
 go4it
发布于 07/13 10:49
字数 1169
阅读 6
收藏 0

本文主要研究一下resilience4j的bulkhead

Bulkhead

resilience4j-bulkhead-0.13.0-sources.jar!/io/github/resilience4j/bulkhead/Bulkhead.java

/**
 *  A Bulkhead instance is thread-safe can be used to decorate multiple requests.
 *
 * A {@link Bulkhead} represent an entity limiting the amount of parallel operations. It does not assume nor does it mandate usage
 * of any particular concurrency and/or io model. These details are left for the client to manage. This bulkhead, depending on the
 * underlying concurrency/io model can be used to shed load, and, where it makes sense, limit resource use (i.e. limit amount of
 * threads/actors involved in a particular flow, etc).
 *
 * In order to execute an operation protected by this bulkhead, a permission must be obtained by calling {@link Bulkhead#isCallPermitted()}
 * If the bulkhead is full, no additional operations will be permitted to execute until space is available.
 *
 * Once the operation is complete, regardless of the result, client needs to call {@link Bulkhead#onComplete()} in order to maintain
 * integrity of internal bulkhead state.
 *
 */
public interface Bulkhead {

    /**
     * Dynamic bulkhead configuration change.
     * NOTE! New `maxWaitTime` duration won't affect threads that are currently waiting for permission.
     * @param newConfig new BulkheadConfig
     */
    void changeConfig(BulkheadConfig newConfig);

    /**
     * Attempts to acquire a permit, which allows an call to be executed.
     *
     * @return boolean whether a call should be executed
     */
    boolean isCallPermitted();

    /**
     * Records a completed call.
     */
    void onComplete();

    /**
     * Returns the name of this bulkhead.
     *
     * @return the name of this bulkhead
     */
    String getName();

    /**
     * Returns the BulkheadConfig of this Bulkhead.
     *
     * @return bulkhead config
     */
    BulkheadConfig getBulkheadConfig();

    /**
     * Get the Metrics of this Bulkhead.
     *
     * @return the Metrics of this Bulkhead
     */
    Metrics getMetrics();

    /**
     * Returns an EventPublisher which subscribes to the reactive stream of BulkheadEvent and
     * can be used to register event consumers.
     *
     * @return an EventPublisher
     */
    EventPublisher getEventPublisher();

    //......

    /**
     * Returns a callable which is decorated by a bulkhead.
     *
     * @param bulkhead the bulkhead
     * @param callable the original Callable
     * @param <T> the result type of callable
     *
     * @return a supplier which is decorated by a Bulkhead.
     */
    static <T> Callable<T> decorateCallable(Bulkhead bulkhead, Callable<T> callable){
        return () -> {
            BulkheadUtils.isCallPermitted(bulkhead);
            try {
                return callable.call();
            }
            finally {
                bulkhead.onComplete();
            }
        };
    }

    /**
     * Returns a supplier which is decorated by a bulkhead.
     *
     * @param bulkhead the bulkhead
     * @param supplier the original supplier
     * @param <T> the type of results supplied by this supplier
     *
     * @return a supplier which is decorated by a Bulkhead.
     */
    static <T> Supplier<T> decorateSupplier(Bulkhead bulkhead, Supplier<T> supplier){
        return () -> {
            BulkheadUtils.isCallPermitted(bulkhead);
            try {
                return supplier.get();
            }
            finally {
                bulkhead.onComplete();
            }
        };
    }

    interface Metrics {


        /**
         * Returns the number of parallel executions this bulkhead can support at this point in time.
         *
         * @return remaining bulkhead depth
         */
        int getAvailableConcurrentCalls();
    }

    /**
     * An EventPublisher which can be used to register event consumers.
     */
    interface EventPublisher extends io.github.resilience4j.core.EventPublisher<BulkheadEvent> {

        EventPublisher onCallRejected(EventConsumer<BulkheadOnCallRejectedEvent> eventConsumer);

        EventPublisher onCallPermitted(EventConsumer<BulkheadOnCallPermittedEvent> eventConsumer);

        EventPublisher onCallFinished(EventConsumer<BulkheadOnCallFinishedEvent> eventConsumer);
    }

    //......
}
  • 这个接口定义了isCallPermitted以及onComplete方法
  • 之后还定义了许多decorate开头的方法,主要是在调用之前先执行BulkheadUtils.isCallPermitted(bulkhead),然后在执行完成调用bulkhead.onComplete();
  • decorate的方法有decorateCheckedSupplier、decorateCompletionStage、decorateCheckedRunnable、decorateCallable、decorateSupplier、decorateConsumer、decorateCheckedConsumer、decorateRunnable、decorateFunction、decorateCheckedFunction。
  • 另外还定义了Metrics接口及EventPublisher接口。

BulkheadUtils.isCallPermitted

resilience4j-bulkhead-0.13.0-sources.jar!/io/github/resilience4j/bulkhead/utils/BulkheadUtils.java

public final class BulkheadUtils {

    public static void isCallPermitted(Bulkhead bulkhead) {
        if(!bulkhead.isCallPermitted()) {
            throw new BulkheadFullException(String.format("Bulkhead '%s' is full", bulkhead.getName()));
        }
    }
}

通过bulkhead.isCallPermitted()进行判断,不通过则抛出BulkheadFullException

SemaphoreBulkhead

resilience4j-bulkhead-0.13.0-sources.jar!/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java

/**
 * A Bulkhead implementation based on a semaphore.
 */
public class SemaphoreBulkhead implements Bulkhead {

    private final String name;
    private final Semaphore semaphore;
    private final Object configChangesLock = new Object();
    private volatile BulkheadConfig config;
    private final BulkheadMetrics metrics;
    private final BulkheadEventProcessor eventProcessor;

    /**
     * Creates a bulkhead using a configuration supplied
     *
     * @param name           the name of this bulkhead
     * @param bulkheadConfig custom bulkhead configuration
     */
    public SemaphoreBulkhead(String name, BulkheadConfig bulkheadConfig) {
        this.name = name;
        this.config = bulkheadConfig != null ? bulkheadConfig
                : BulkheadConfig.ofDefaults();
        // init semaphore
        this.semaphore = new Semaphore(this.config.getMaxConcurrentCalls(), true);

        this.metrics = new BulkheadMetrics();
        this.eventProcessor = new BulkheadEventProcessor();
    }

    /**
     * Creates a bulkhead with a default config.
     *
     * @param name the name of this bulkhead
     */
    public SemaphoreBulkhead(String name) {
        this(name, BulkheadConfig.ofDefaults());
    }

    /**
     * Create a bulkhead using a configuration supplier
     *
     * @param name           the name of this bulkhead
     * @param configSupplier BulkheadConfig supplier
     */
    public SemaphoreBulkhead(String name, Supplier<BulkheadConfig> configSupplier) {
        this(name, configSupplier.get());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void changeConfig(final BulkheadConfig newConfig) {
        synchronized (configChangesLock) {
            int delta =  newConfig.getMaxConcurrentCalls() - config.getMaxConcurrentCalls();
            if (delta < 0) {
                semaphore.acquireUninterruptibly(-delta);
            } else if (delta > 0) {
                semaphore.release(delta);
            }
            config = newConfig;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isCallPermitted() {

        boolean callPermitted = tryEnterBulkhead();

        publishBulkheadEvent(
                () -> callPermitted ? new BulkheadOnCallPermittedEvent(name)
                        : new BulkheadOnCallRejectedEvent(name)
        );

        return callPermitted;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onComplete() {
        semaphore.release();
        publishBulkheadEvent(() -> new BulkheadOnCallFinishedEvent(name));
    }

    boolean tryEnterBulkhead() {

        boolean callPermitted = false;
        long timeout = config.getMaxWaitTime();

        if (timeout == 0) {
            callPermitted = semaphore.tryAcquire();
        } else {
            try {
                callPermitted = semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS);
            } catch (InterruptedException ex) {
                callPermitted = false;
            }
        }
        return callPermitted;
    }

    private void publishBulkheadEvent(Supplier<BulkheadEvent> eventSupplier) {
        if (eventProcessor.hasConsumers()) {
            eventProcessor.consumeEvent(eventSupplier.get());
        }
    }

    private final class BulkheadMetrics implements Metrics {
        private BulkheadMetrics() {
        }

        @Override
        public int getAvailableConcurrentCalls() {
            return semaphore.availablePermits();
        }
    }

    //......
}
  • SemaphoreBulkhead是使用信号量实现的Bulkhead
  • Semaphore的大小为BulkheadConfig的maxConcurrentCalls
  • isCallPermitted方法会调用tryEnterBulkhead方法,然后发布BulkheadOnCallPermittedEvent事件
  • tryEnterBulkhead方法主要是对semaphore进行tryAcquire,如果配置的maxWaitTime不为0,则按指定时间timeout获取
  • onComplete方法主要是释放信号量,然后发布一个BulkheadOnCallFinishedEvent事件
  • 使用publishBulkheadEvent发布事件,是委托给eventProcessor.consumeEvent处理,这个processor是BulkheadEventProcessor
  • BulkheadMetrics重写了getAvailableConcurrentCalls接口,返回的是semaphore.availablePermits()

BulkheadEventProcessor

resilience4j-bulkhead-0.13.0-sources.jar!/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java

    private class BulkheadEventProcessor extends EventProcessor<BulkheadEvent> implements EventPublisher, EventConsumer<BulkheadEvent> {

        @Override
        public EventPublisher onCallPermitted(EventConsumer<BulkheadOnCallPermittedEvent> onCallPermittedEventConsumer) {
            registerConsumer(BulkheadOnCallPermittedEvent.class, onCallPermittedEventConsumer);
            return this;
        }

        @Override
        public EventPublisher onCallRejected(EventConsumer<BulkheadOnCallRejectedEvent> onCallRejectedEventConsumer) {
            registerConsumer(BulkheadOnCallRejectedEvent.class, onCallRejectedEventConsumer);
            return this;
        }

        @Override
        public EventPublisher onCallFinished(EventConsumer<BulkheadOnCallFinishedEvent> onCallFinishedEventConsumer) {
            registerConsumer(BulkheadOnCallFinishedEvent.class, onCallFinishedEventConsumer);
            return this;
        }

        @Override
        public void consumeEvent(BulkheadEvent event) {
            super.processEvent(event);
        }
    }
  • BulkheadEventProcessor继承了EventProcessor,实现了EventPublisher及EventConsumer接口

小结

  • resilience4j的bulkhead,本质上是对方法进行分并发调用的控制,SemaphoreBulkhead采用的是信号量的实现,信号量大小为maxConcurrentCalls,执行之前获取下信号量,获取不到抛出异常,获取到的话,在执行之后释放信号量
  • Bulkhead定义了一系列decorate开头的静态方法来对callable、runnable、supplier等进行包装,植入对信号量的获取及释放逻辑。

doc

© 著作权归作者所有

共有 人打赏支持
go4it
粉丝 70
博文 789
码字总数 624346
作品 0
深圳
私信 提问
resilience4j小试牛刀

序 本文主要研究下resilience4j的基本功能 maven CircuitBreaker CircuitBreaker主要是实现针对接口异常的断路统计以及断路处理 Timelimiter 主要是实现超时的控制 Bulkhead Bulkhead目前来看...

go4it
07/09
0
0
聊聊resilience4j的Retry

序 本文主要研究一下resilience4j的Retry Retry resilience4j-retry-0.13.0-sources.jar!/io/github/resilience4j/retry/Retry.java 这个类定义了一些工厂方法,最后new的是RetryImpl 还定义...

go4it
07/14
0
0
聊聊resilience4j的CircuitBreakerStateMachine

序 本文主要研究一下resilience4j的CircuitBreakerStateMachine CircuitBreakerStateMachine resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/int......

go4it
07/12
0
0
聊聊resilience4j的CircuitBreakerConfig

序 本文主要研究一下resilience4j的CircuitBreakerConfig CircuitBreakerConfig resilience4j-circuitbreaker-0.13.0-sources.jar!/io/github/resilience4j/circuitbreaker/CircuitBreakerC......

go4it
07/10
0
0
聊聊resilience4j的fallback

序 本文主要研究一下resilience4j的fallback 使用实例 Try vavr-0.9.2-sources.jar!/io/vavr/control/Try.java 这个Try继承了Value接口 另外就是提供了一些静态工厂方法,ofSupplier方法会触...

go4it
07/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Gradle 提速:每天为你省下一杯喝咖啡的时间

摘要: ### 前言 作为一名 Android 开发同学,当你的工程和代码达到一定规模的时候,相信你一定遇到过编译速度过慢的问题。比如: ![](https://user-gold-cdn.xitu.io/2018/11/6/166e7021c0...

阿里云官方博客
17分钟前
0
0
在 PHPStorm 中如何对远程服务使用 xdebug 进行断点调试

1. 安装 xdebug 1.1. 下载、安装 ➜ ~ wget https://xdebug.org/files/xdebug-2.4.1.tgz➜ ~ tar zxf xdebug-2.4.1.tgz➜ ~ cd xdebug-2.4.1➜ ~ phpize➜ ~ ./configure --with-ph......

whoru
19分钟前
1
0
看完这篇文章还不懂Python装饰器?

1、必备 2.需求来了 初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功...

糖宝lsh
25分钟前
3
0
Bytom合约预编译

比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom 在开发合约的过程中你们有没有遇到一些问题呢?比如编译合约的过程中不能...

比原链Bytom
26分钟前
2
0
supervisor安装配置

supervisor安装配置 安装 wget -c https://files.pythonhosted.org/packages/44/60/698e54b4a4a9b956b2d709b4b7b676119c833d811d53ee2500f1b5e96dc3/supervisor-3.3.4.tar.gztar -zxvf su......

jackmanwu
52分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部