/* * * Copyright 2017 Robert Winkler, Lucas Lech * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ package io.github.resilience4j.bulkhead.internal; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadConfig; import io.github.resilience4j.bulkhead.event.BulkheadEvent; import io.github.resilience4j.bulkhead.event.BulkheadOnCallPermittedEvent; import io.github.resilience4j.bulkhead.event.BulkheadOnCallRejectedEvent; import io.reactivex.Flowable; import io.reactivex.processors.FlowableProcessor; import io.reactivex.processors.PublishProcessor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** * A Bulkhead implementation based on a semaphore. */ public class SemaphoreBulkhead implements Bulkhead{ private final String name; private final Semaphore semaphore; private final BulkheadConfig bulkheadConfig; private final FlowableProcessor<BulkheadEvent> eventPublisher; private final BulkheadMetrics metrics; /** * 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.bulkheadConfig = bulkheadConfig != null ? bulkheadConfig : BulkheadConfig.ofDefaults(); // init semaphore this.semaphore = new Semaphore(this.bulkheadConfig.getMaxConcurrentCalls(), true); // init event publisher this.eventPublisher = PublishProcessor.<BulkheadEvent>create() .toSerialized(); this.metrics = new BulkheadMetrics(); } /** * 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()); } @Override public boolean isCallPermitted() { boolean callPermitted = tryEnterBulkhead(); publishBulkheadEvent( () -> callPermitted ? new BulkheadOnCallPermittedEvent(name) : new BulkheadOnCallRejectedEvent(name) ); return callPermitted; } @Override public void onComplete() { semaphore.release(); } @Override public String getName() { return this.name; } @Override public BulkheadConfig getBulkheadConfig() { return bulkheadConfig; } @Override public Metrics getMetrics() { return metrics; } @Override public Flowable<BulkheadEvent> getEventStream() { return eventPublisher; } @Override public String toString() { return String.format("Bulkhead '%s'", this.name); } boolean tryEnterBulkhead() { boolean callPermitted = false; long timeout = bulkheadConfig.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(eventPublisher.hasSubscribers()) { eventPublisher.onNext(eventSupplier.get()); } } private final class BulkheadMetrics implements Metrics { private BulkheadMetrics() { } @Override public int getAvailableConcurrentCalls() { return semaphore.availablePermits(); } } }