1

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.)

Marnee
  • 370
  • 1
  • 4
  • 15

1 Answers1

1

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:

  • Use a BeanPostProcessor in your @Configuration to get a handle of the fully wired EnvironmentController
  • Wrap your own CircuitBreaker implementation with Resilience4j around the EnvirommentController's methods that you are interested in
  • Profit

If 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);
            });
        }
    }
}
Nikola Yovchev
  • 9,498
  • 4
  • 46
  • 72
  • 1
    Thank you. I was able to create a post processor and find my EnvironmentController bean. But I still don't know how to hook up its labelled method to the circuit breaker. Would you be able to provide sample code for that piece? – Marnee Jul 07 '21 at 20:00
  • Note that I clarified my original question to make it clear I was asking about Resilience4j circuit breaker (since they also have other products). – Marnee Jul 07 '21 at 20:29
  • I gave that a try. I've never used lombok before. I get error at runtime on the "return new CircuitBreakerFriendlyEnvironmentController" because the constructor does not exist. It does not seem to recognize the RequiredArgsConstructor. Is there a trick to it? I see there is a Lombok plugin bundled in Intellij, so that shouldn't be the issue. If you don't know offhand, I'll keep digging. Thanks much! I did investigate extending EnvironmentController, but it was messy. I would never have come up with this solution. – Marnee Jul 07 '21 at 23:21
  • 1
    Glad that helped as a direction, even if not with a full solution. As far as I know, you gotta enable intellij lombok annotations for it to understand the RequiredArgsConstructor but should be fairly straightforward. To be honest, Controllers are fairly easy to extend/wrap around if you own the source code, but a bit clunky to extend/change if they come out of a library, so I think unfortunately a degree of mess is unavoidable :( – Nikola Yovchev Jul 08 '21 at 20:13