Given Hystrix going into mainentance mode, I've been working on migrating a (rather large) codebase to Resilience4j.
I make heavy use of the following pattern with Hystrix:
new HystrixCommand<SomeReturnValue>(DependencyKeys.DEPENDENCY) {
@Override
protected SomeReturnValue run() {
return someExpensiveCall();
}
}
.observe()
And I want to replicate some of Hystrix' functionality with Resilience4j.
So far I have the following syntax to wire up an external call:
resilience.single(DependencyKeys.DEPENDENCY, this::someExpensiveCall);
Where the Resilience
class provides the single
method:
public <T> Single<T> single(ResilienceKey key, Callable<T> callable) {
return Completable.complete()
.subscribeOn(Schedulers.computation())
.observeOn(configuration.scheduler(key))
.andThen(Single.defer(() -> Single.fromCallable(callable)
.lift(CircuitBreakerOperator.of(configuration.circuitBreaker(key)))
.lift(RateLimiterOperator.of(configuration.rateLimiter(key)))
.lift(BulkheadOperator.of(configuration.bulkhead(key)))
))
.observeOn(Schedulers.computation());
}
How could this look to better resemble what you get with Hystrix in terms of circuit breaking and running the code on different thread pools, but in a more sane way. I really don't like starting the chain off with a Completable.complete()
just so I can force the observeOn
before the actual callable is wrapped.
.lift(CircuitBreakerOperator.of(configuration.circuitBreaker(key)))
operators are effectively wrapping the deferred async function. I guess the question I'm really asking is: assuming this works, is it best practice from an RxJava2 and Resilience4j point of view. – Guzel