序
本文主要研究一下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 the result type of callable
*
* @return a supplier which is decorated by a Bulkhead.
*/
static Callable decorateCallable(Bulkhead bulkhead, Callable 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 the type of results supplied by this supplier
*
* @return a supplier which is decorated by a Bulkhead.
*/
static Supplier decorateSupplier(Bulkhead bulkhead, Supplier 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 {
EventPublisher onCallRejected(EventConsumer eventConsumer);
EventPublisher onCallPermitted(EventConsumer eventConsumer);
EventPublisher onCallFinished(EventConsumer 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 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 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 implements EventPublisher, EventConsumer {
@Override
public EventPublisher onCallPermitted(EventConsumer onCallPermittedEventConsumer) {
registerConsumer(BulkheadOnCallPermittedEvent.class, onCallPermittedEventConsumer);
return this;
}
@Override
public EventPublisher onCallRejected(EventConsumer onCallRejectedEventConsumer) {
registerConsumer(BulkheadOnCallRejectedEvent.class, onCallRejectedEventConsumer);
return this;
}
@Override
public EventPublisher onCallFinished(EventConsumer 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
- Resilience4j is a fault tolerance library designed for Java8 and functional programming