This library is a lightweight, easy-to-use fault tolerance library inspired by Netflix Hystrix, but designed solely for Java 8 and functional programming. Lightweight, because the library only uses SLF4J and a functional library for Java 8 called Javaslang. Javaslang itself has no external library dependencies.
The library provides several higher-order functions to decorate any function with a Circuit Breaker. In the following I call the higher-order functions decorators. The decorators return an enhanced version of your function. Furthermore, the library provides a decorator to retry failed functions. You can stack more than one decorator on any given function. That means, you can combine a Retry decorator with a CircuitBreaker decorator. Any decorated function can be invoked synchronously or asynchronously.
The CircuitBreaker is implemented via a finite state machine with three states: CLOSED
, OPEN
and HALF_OPEN
.
The CircuitBreaker does not know anything about the backend’s state by itself, but uses the information provided by the decorators via CircuitBreaker::recordSuccess()
and CircuitBreaker::recordFailure(throwable)
. See example:
static <T> Supplier<T> decorateSupplier(Supplier<T> supplier, CircuitBreaker circuitBreaker){
return () -> {
circuitBreaker.isCallPermitted();
try {
T returnValue = supplier.get();
circuitBreaker.recordSuccess();
return returnValue;
} catch (Exception exception) {
circuitBreaker.recordFailure(exception);
throw exception;
}
};
}
The state of the CircuitBreaker changes from CLOSED
to OPEN
when the failure rate is above a (configurable) threshold.
Then, all access to the backend is blocked for a (configurable) time duration. CircuitBreaker::isCallPermitted()
throws a CircuitBreakerOpenException
, if the CircuitBreaker is OPEN
.
The CircuitBreaker uses a Ring Bit Buffer in the CLOSED
state to store the success or failure statuses of the calls. A successful call is stored as a 0
bit and a failed call is stored as a 1
bit. The Ring Bit Buffer has a (configurable) fixed-size. The Ring Bit Buffer uses internally a BitSet to store the bits which is saving memory compared to a boolean array. The BitSet uses a long[] array to store the bits. That means the BitSet only needs an array of 16 long (64-bit) values to store the status of 1024 calls.
The Ring Bit Buffer must be full, before the failure rate can be calculated.
For example, if the size of the Ring Buffer is 10, then at least 10 calls must evaluated, before the failure rate can be calculated. If only 9 calls have been evaluated the CircuitBreaker will not trip open even if all 9 calls have failed.
After the time duration has elapsed, the CircuitBreaker state changes from OPEN
to HALF_CLOSED
and allows calls to see if the backend is still unavailable or has become available again. The CircuitBreaker uses another (configurable) Ring Bit Buffer to evaluate the failure rate in the HALF_CLOSED
state. If the failure rate is above the configured threshold, the state changes back to OPEN
. If the failure rate is below or equal to the threshold, the state changes back to CLOSED
.
CircuitBreaker::recordFailure(exception)
checks if the exception should be recorded as a failure or should be ignored. You can configure a custom Predicate
which decides whether an exception should be recorded as a failure. The default Predicate records all exceptions as a failure.
See User Guide.
Copyright 2015 Robert Winkler
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.