/* * Copyright 2017 Dan Maas * * 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.ratpack; import com.codahale.metrics.MetricRegistry; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.matcher.Matchers; import com.google.inject.multibindings.OptionalBinder; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.metrics.CircuitBreakerMetrics; import io.github.resilience4j.metrics.RateLimiterMetrics; import io.github.resilience4j.metrics.RetryMetrics; import io.github.resilience4j.prometheus.CircuitBreakerExports; import io.github.resilience4j.prometheus.RateLimiterExports; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import io.github.resilience4j.ratpack.circuitbreaker.CircuitBreaker; import io.github.resilience4j.ratpack.circuitbreaker.CircuitBreakerMethodInterceptor; import io.github.resilience4j.ratpack.ratelimiter.RateLimiter; import io.github.resilience4j.ratpack.ratelimiter.RateLimiterMethodInterceptor; import io.github.resilience4j.ratpack.retry.Retry; import io.github.resilience4j.ratpack.retry.RetryMethodInterceptor; import io.github.resilience4j.retry.RetryRegistry; import io.prometheus.client.CollectorRegistry; import ratpack.api.Nullable; import ratpack.dropwizard.metrics.DropwizardMetricsModule; import ratpack.guice.ConfigurableModule; import ratpack.service.Service; import ratpack.service.StartEvent; import javax.inject.Inject; import javax.inject.Singleton; /** * This module registers class and method interceptors for circuit breakers, rate limiters, and retries. * <p> * This module also registers metrics: * - circuitbreaker, ratelimiter, and retry metrics with dropwizard metrics, if enabled. * - circuitbreaker, ratelimiter, and retry metrics with prometheus, if enabled. * <p> * Only enable metrics if you have dependencies for resilience4j-metrics in the classpath and an instance of * {@link MetricRegistry} is bound (usually this will happen when installing {@link DropwizardMetricsModule}). * This must be done manually, since guice doesn't know if dropwizard is on the runtime classpath. * <p> * Only enable prometheus if you have a dependency on resilience4j-prometheus in the classpath and an instance of * {@link CollectorRegistry} is bound. This must be done manually, since guice doesn't know if prometheus is on the runtime classpath. * <p> * Also note that for this to work, CircuitBreaker, RateLimiter, and Retry instances must be created * before the respective registries are bound. */ public class Resilience4jModule extends ConfigurableModule<Resilience4jConfig> { @Override protected void configure() { bindInterceptor(Matchers.any(), Matchers.annotatedWith(CircuitBreaker.class), injected(new CircuitBreakerMethodInterceptor())); bindInterceptor(Matchers.any(), Matchers.annotatedWith(RateLimiter.class), injected(new RateLimiterMethodInterceptor())); bindInterceptor(Matchers.any(), Matchers.annotatedWith(Retry.class), injected(new RetryMethodInterceptor())); bindInterceptor(Matchers.annotatedWith(CircuitBreaker.class), Matchers.any(), injected(new CircuitBreakerMethodInterceptor())); bindInterceptor(Matchers.annotatedWith(RateLimiter.class), Matchers.any(), injected(new RateLimiterMethodInterceptor())); bindInterceptor(Matchers.annotatedWith(Retry.class), Matchers.any(), injected(new RetryMethodInterceptor())); OptionalBinder.newOptionalBinder(binder(), CircuitBreakerRegistry.class).setDefault().toInstance(CircuitBreakerRegistry.ofDefaults()); OptionalBinder.newOptionalBinder(binder(), RateLimiterRegistry.class).setDefault().toInstance(RateLimiterRegistry.ofDefaults()); OptionalBinder.newOptionalBinder(binder(), RetryRegistry.class).setDefault().toInstance(RetryRegistry.ofDefaults()); bind(Resilience4jService.class); } private <T> T injected(T instance) { requestInjection(instance); return instance; } @Provides @Singleton @Nullable public CircuitBreakerMetrics circuitBreakerMetrics(CircuitBreakerRegistry circuitBreakerRegistry, Resilience4jConfig config) { if (config.isMetrics()) { return CircuitBreakerMetrics.ofCircuitBreakerRegistry(circuitBreakerRegistry); } else { return null; } } @Provides @Singleton @Nullable public RateLimiterMetrics rateLimiterMetrics(RateLimiterRegistry rateLimiterRegistry, Resilience4jConfig config) { if (config.isMetrics()) { return RateLimiterMetrics.ofRateLimiterRegistry(rateLimiterRegistry); } else { return null; } } @Provides @Singleton @Nullable public RetryMetrics retryMetrics(RetryRegistry retryRegistry, Resilience4jConfig config) { if (config.isMetrics()) { return RetryMetrics.ofRetryRegistry(retryRegistry); } else { return null; } } @Provides @Singleton @Nullable public CircuitBreakerExports circuitBreakerExports(CircuitBreakerRegistry circuitBreakerRegistry, Resilience4jConfig config) { if (config.isPrometheus()) { return CircuitBreakerExports.ofCircuitBreakerRegistry(circuitBreakerRegistry); } else { return null; } } @Provides @Singleton @Nullable public RateLimiterExports rateLimiterExports(RateLimiterRegistry rateLimiterRegistry, Resilience4jConfig config) { if (config.isPrometheus()) { return RateLimiterExports.ofRateLimiterRegistry(rateLimiterRegistry); } else { return null; } } private static class Resilience4jService implements Service { private final Injector injector; private final Resilience4jConfig config; @Inject public Resilience4jService(Injector injector, Resilience4jConfig config) { this.injector = injector; this.config = config; } @Override public void onStart(StartEvent event) throws Exception { if (config.isMetrics() && injector.getExistingBinding(Key.get(MetricRegistry.class)) != null) { MetricRegistry metricRegistry = injector.getInstance(MetricRegistry.class); metricRegistry.registerAll(injector.getInstance(CircuitBreakerMetrics.class)); metricRegistry.registerAll(injector.getInstance(RateLimiterMetrics.class)); metricRegistry.registerAll(injector.getInstance(RetryMetrics.class)); } if (config.isPrometheus() && injector.getExistingBinding(Key.get(CollectorRegistry.class)) != null) { CollectorRegistry collectorRegistry = injector.getInstance(CollectorRegistry.class); injector.getInstance(CircuitBreakerExports.class).register(collectorRegistry); injector.getInstance(RateLimiterExports.class).register(collectorRegistry); } } } }