How can I decorate a service method in an external library with Resilience4j circuit breaker? Specifically, I want to decorate a method in spring cloud config server (the server itself, not client code): org.springframework.cloud.config.server.environment.EnvironmentController.labelled(...)
. It takes multiple parameters. I can't annotate it because it is in a 3rd-party library.
EnvironmentController
class is a Spring @RestController
.
I see methods in CircuitBreaker
for decorating Callables, Functions, etc., but none that seem to apply to this. (I'm completely new to Resilience4j, so I'm hoping I'm missing a simple solution.)
A common approach (not only to Resilience4j
, but to Spring
in general) would be to use a BeanPostProcessor
(you can see a non-related example here).
Then in the beanPostProcessor, you can get a handle of your EnvironmentController
and wrap around its implementation/method with your Resilience4j
CicrcuitBreaking logic.
Basically the approach would be something like:
BeanPostProcessor
in your @Configuration
to get a handle of the fully wired EnvironmentController
CircuitBreaker
implementation with Resilience4j around the EnvirommentController
's methods that you are interested inIf that doesn't fully clear the picture, I can add some sample code to get you started, just let me know. Keep in mind that's probably just one of the many ways this can be approached.
Edit:
Some code (not sure if it works, haven't tested, spring-boot
is notoriously annoying in terms of using a shit-ton of aop
for the MVC mapping autoconfig, so you might have to play around with aspects or proxy config instead), might look like (keep in mind I'd use lombok to avoid all the boilerplate, for posterity):
@Configuration
@Slf4j
public class MyConfig {
// or however the heck you get your breaker config
private final CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom().build();
private final CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
private final CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("suchBreakerMuchWow");
@Bean
public CircuitBreakerAwarePostProcessor circuitBreakerAwarePostProcessor() {
return new CircuitBreakerAwarePostProcessor();
}
public class CircuitBreakerAwarePostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof EnvironmentController) {
return new CircuitBreakerFriendlyEnvironmentController((EnvironmentController) bean);
}
return bean;
}
}
private interface Exclude {
Environment labelled (String name, String profiles, String label);
}
@RequiredArgsConstructor
private class CircuitBreakerFriendlyEnvironmentController implements Exclude {
@Delegate(types = EnvironmentController.class, excludes = Exclude.class)
@NonNull private final EnvironmentController environmentController;
@Override
public Environment labelled(String name, String profiles, String label) {
return circuitBreaker.executeSupplier(() -> {
log.info("such wow");
return environmentController.labelled(name, profiles, label);
});
}
}
}