Skip to content

Commit 4e9ddc3

Browse files
drmaasRobWin
drmaas
authored andcommitted
* added circuitbreaker configs to ratpack module
* added ratelimiter configs to ratpack module * added retry configs to ratpack module closes ReactiveX#123
1 parent b906511 commit 4e9ddc3

File tree

9 files changed

+827
-25
lines changed

9 files changed

+827
-25
lines changed

resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreaker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
*
4040
* A {@link CircuitBreaker} manages the state of a backend system.
4141
* The CircuitBreaker is implemented via a finite state machine with three states: CLOSED, OPEN and HALF_OPEN.
42-
* The CircuitBreaker does not know anything about the backends state by itself, but uses the information provided by the decorators via
42+
* The CircuitBreaker does not know anything about the backend's state by itself, but uses the information provided by the decorators via
4343
* {@link CircuitBreaker#onSuccess} and {@link CircuitBreaker#onError} events.
4444
* Before communicating with the backend, the the permission to do so must be obtained via the method {@link CircuitBreaker#isCallPermitted()}.
4545
*

resilience4j-documentation/src/docs/asciidoc/addon_guides/ratpack.adoc

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,31 @@ dependencies {
1818
}
1919
```
2020

21+
==== Basic Usage
22+
23+
Installing the `Resilience4jModule` module provides a `CircuitBreakerRegistry`, `RateLimiterRegistry`,
24+
and `RetryRegistry` with the default configurations. It also install the Guice method interceptors
25+
for CircuitBreakers, RateLimiters, and Retries. Finally, it allows configuration of metrics
26+
and even the building of CircuitBreakers, RateLimiters, and Retries. See below for configuration details.
27+
28+
Note: If you don't register a `CircuitBreakerRegistry` or `RateLimiterRegistry` or `RetryRegistry`, the defaults
29+
will be used.
30+
31+
For example
32+
33+
[source,java]
34+
----
35+
public class MyModule extends AbstractModule {
36+
37+
@Override
38+
protected void configure() {
39+
CircuitBreakerConfig config = CircuitBreakerConfig.custom();
40+
bind(CircuitBreakerRegistry.class).toInstance(CircuitBreakerRegistry.of(config);
41+
install(Resilience4jModule.class);
42+
}
43+
}
44+
----
45+
2146
==== Handlers
2247

2348
You can rate limit an endpoint by defining a `RateLimiterHandler` for the endpoint.
@@ -187,6 +212,92 @@ public class BusinessBService implements BusinessService {
187212
}
188213
----
189214

215+
==== Adding CircuitBreakers, RateLimiters, and Retries
216+
These can be defined in the module configuration or in an external configuration.
217+
Note that the module only provide default registries, which you can replace by
218+
binding your own.
219+
220+
Module configuration example:
221+
222+
[source,java]
223+
----
224+
public class MyModule extends AbstractModule {
225+
226+
@Override
227+
protected void configure() {
228+
Resilience4jModule module = new Resilience4jModule();
229+
module.configure(c -> c
230+
.circuitBreaker("test1", cb -> cb
231+
.defaults(true)
232+
).circuitBreaker("test2", cb -> cb
233+
.failureRateThreshold(50)
234+
.waitIntervalInMillis(5000)
235+
.ringBufferSizeInClosedState(200)
236+
.ringBufferSizeInHalfOpenState(20)
237+
).rateLimiter("test1", cb -> cb
238+
.defaults(true)
239+
).rateLimiter("test2", cb -> cb
240+
.limitForPeriod(100)
241+
.limitRefreshPeriodInNanos(500)
242+
.timeoutInMillis(10)
243+
).retry("test1", cb -> cb
244+
.defaults(true)
245+
).retry("test2", cb -> cb
246+
.maxAttempts(3)
247+
.waitDurationInMillis(1000)
248+
)
249+
);
250+
install(module);
251+
}
252+
}
253+
----
254+
255+
External configuration example:
256+
257+
[source,groovy]
258+
----
259+
ratpack {
260+
serverConfig {
261+
yaml(getClass().classLoader.getResource('application.yml'))
262+
require("/resilience4j", Resilience4jConfig)
263+
}
264+
bindings {
265+
module(Resilience4jModule)
266+
}
267+
handlers {
268+
get {
269+
render 'ok'
270+
}
271+
}
272+
}
273+
----
274+
275+
[source,yaml]
276+
----
277+
resilience4j:
278+
circuitBreakers:
279+
test1:
280+
defaults: true
281+
test2:
282+
ringBufferSizeInClosedState: 200
283+
ringBufferSizeInHalfOpenState: 20
284+
waitInterval: 5000
285+
failureRateThreshold: 50
286+
rateLimiters:
287+
test1:
288+
defaults: true
289+
test2:
290+
limitForPeriod: 100
291+
limitRefreshPeriodInNanos: 500
292+
timeoutInMillis: 10
293+
retries:
294+
test1:
295+
defaults: true
296+
test2:
297+
maxAttempts: 3
298+
waitDurationInMillis: 1000
299+
----
300+
190301
==== Metrics
191302
Both dropwizard and prometheus metrics can be auto configured and enabled for all registered
192303
circuitbreaker instances, ratelimiter instances, and retry instances.
@@ -237,29 +348,6 @@ Ratpack provides the concept of a health check. See https://ratpack.io/manual/cu
237348
CircuitBreaker, RateLimiter, and Retry health checks have not been implemented yet.
238349
When implemented they should function similarly to the spring boot health checks.
239350

240-
==== Configuration
241-
242-
You must install the `Resilience4jModule` Guice Module if you want to use AOP so that the method interceptors work.
243-
You can optionally configure your CircuitBreakers, RateLimiters, and Retries in your Guice Module as well.
244-
245-
Note: If you don't register a `CircuitBreakerRegistry` or `RateLimiterRegistry`, the defaults
246-
will be used when using AOP.
247-
248-
For example
249-
250-
[source,java]
251-
----
252-
public class MyModule extends AbstractModule {
253-
254-
@Override
255-
protected void configure() {
256-
CircuitBreakerConfig config = CircuitBreakerConfig.custom();
257-
bind(CircuitBreakerRegistry.class).toInstance(CircuitBreakerRegistry.of(config);
258-
install(Resilience4jModule.class);
259-
}
260-
}
261-
----
262-
263351
==== CircuitBreaker Event Monitoring
264352
TODO
265353
Handlers for displaying the last X CircuitBreaker, Ratelimiter, or Retry events are not yet implemented.

resilience4j-ratpack/src/main/java/io/github/resilience4j/ratpack/Resilience4jConfig.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,53 @@
1616

1717
package io.github.resilience4j.ratpack;
1818

19+
import io.github.resilience4j.ratpack.circuitbreaker.CircuitBreakerConfig;
20+
import io.github.resilience4j.ratpack.ratelimiter.RateLimiterConfig;
21+
import io.github.resilience4j.ratpack.retry.RetryConfig;
22+
import ratpack.func.Function;
23+
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
27+
import static ratpack.util.Exceptions.uncheck;
28+
1929
public class Resilience4jConfig {
30+
private Map<String, CircuitBreakerConfig> circuitBreakers = new HashMap<>();
31+
private Map<String, RateLimiterConfig> rateLimiters = new HashMap<>();
32+
private Map<String, RetryConfig> retries = new HashMap<>();
2033
private boolean metrics = false;
2134
private boolean prometheus = false;
2235

36+
public Resilience4jConfig circuitBreaker(String name, Function<? super CircuitBreakerConfig, ? extends CircuitBreakerConfig> configure) {
37+
try {
38+
CircuitBreakerConfig finalConfig = configure.apply(new CircuitBreakerConfig());
39+
circuitBreakers.put(name, finalConfig);
40+
return this;
41+
} catch (Exception e) {
42+
throw uncheck(e);
43+
}
44+
}
45+
46+
public Resilience4jConfig rateLimiter(String name, Function<? super RateLimiterConfig, ? extends RateLimiterConfig> configure) {
47+
try {
48+
RateLimiterConfig finalConfig = configure.apply(new RateLimiterConfig());
49+
rateLimiters.put(name, finalConfig);
50+
return this;
51+
} catch (Exception e) {
52+
throw uncheck(e);
53+
}
54+
}
55+
56+
public Resilience4jConfig retry(String name, Function<? super RetryConfig, ? extends RetryConfig> configure) {
57+
try {
58+
RetryConfig finalConfig = configure.apply(new RetryConfig());
59+
retries.put(name, finalConfig);
60+
return this;
61+
} catch (Exception e) {
62+
throw uncheck(e);
63+
}
64+
}
65+
2366
public Resilience4jConfig metrics(boolean metrics) {
2467
this.metrics = metrics;
2568
return this;
@@ -30,6 +73,18 @@ public Resilience4jConfig prometheus(boolean prometheus) {
3073
return this;
3174
}
3275

76+
public Map<String, CircuitBreakerConfig> getCircuitBreakers() {
77+
return circuitBreakers;
78+
}
79+
80+
public Map<String, RateLimiterConfig> getRateLimiters() {
81+
return rateLimiters;
82+
}
83+
84+
public Map<String, RetryConfig> getRetries() {
85+
return retries;
86+
}
87+
3388
public boolean isMetrics() {
3489
return metrics;
3590
}

resilience4j-ratpack/src/main/java/io/github/resilience4j/ratpack/Resilience4jModule.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,22 @@
2121
import com.google.inject.Provides;
2222
import com.google.inject.matcher.Matchers;
2323
import com.google.inject.multibindings.OptionalBinder;
24+
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
2425
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
2526
import io.github.resilience4j.metrics.CircuitBreakerMetrics;
2627
import io.github.resilience4j.metrics.RateLimiterMetrics;
2728
import io.github.resilience4j.metrics.RetryMetrics;
2829
import io.github.resilience4j.prometheus.CircuitBreakerExports;
2930
import io.github.resilience4j.prometheus.RateLimiterExports;
31+
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
3032
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
3133
import io.github.resilience4j.ratpack.circuitbreaker.CircuitBreaker;
3234
import io.github.resilience4j.ratpack.circuitbreaker.CircuitBreakerMethodInterceptor;
3335
import io.github.resilience4j.ratpack.ratelimiter.RateLimiter;
3436
import io.github.resilience4j.ratpack.ratelimiter.RateLimiterMethodInterceptor;
3537
import io.github.resilience4j.ratpack.retry.Retry;
3638
import io.github.resilience4j.ratpack.retry.RetryMethodInterceptor;
39+
import io.github.resilience4j.retry.RetryConfig;
3740
import io.github.resilience4j.retry.RetryRegistry;
3841
import io.prometheus.client.CollectorRegistry;
3942
import ratpack.api.Nullable;
@@ -44,6 +47,7 @@
4447

4548
import javax.inject.Inject;
4649
import javax.inject.Singleton;
50+
import java.time.Duration;
4751

4852
/**
4953
* This module registers class and method interceptors for circuit breakers, rate limiters, and retries.
@@ -152,17 +156,64 @@ public Resilience4jService(Injector injector, Resilience4jConfig config) {
152156

153157
@Override
154158
public void onStart(StartEvent event) throws Exception {
159+
160+
// build circuit breakers
161+
CircuitBreakerRegistry circuitBreakerRegistry = injector.getInstance(CircuitBreakerRegistry.class);
162+
config.getCircuitBreakers().forEach((name, circuitBreakerConfig) -> {
163+
if (circuitBreakerConfig.getDefaults()) {
164+
circuitBreakerRegistry.circuitBreaker(name);
165+
} else {
166+
circuitBreakerRegistry.circuitBreaker(name, CircuitBreakerConfig.custom()
167+
.failureRateThreshold(circuitBreakerConfig.getFailureRateThreshold())
168+
.ringBufferSizeInClosedState(circuitBreakerConfig.getRingBufferSizeInClosedState())
169+
.ringBufferSizeInHalfOpenState(circuitBreakerConfig.getRingBufferSizeInHalfOpenState())
170+
.waitDurationInOpenState(Duration.ofMillis(circuitBreakerConfig.getWaitIntervalInMillis()))
171+
.build());
172+
}
173+
});
174+
175+
// build rate limiters
176+
RateLimiterRegistry rateLimiterRegistry = injector.getInstance(RateLimiterRegistry.class);
177+
config.getRateLimiters().forEach((name, rateLimiterConfig) -> {
178+
if (rateLimiterConfig.getDefaults()) {
179+
rateLimiterRegistry.rateLimiter(name);
180+
} else {
181+
rateLimiterRegistry.rateLimiter(name, RateLimiterConfig.custom()
182+
.limitForPeriod(rateLimiterConfig.getLimitForPeriod())
183+
.limitRefreshPeriod(Duration.ofNanos(rateLimiterConfig.getLimitRefreshPeriodInNanos()))
184+
.timeoutDuration(Duration.ofMillis(rateLimiterConfig.getTimeoutInMillis()))
185+
.build());
186+
}
187+
});
188+
189+
// build retries
190+
RetryRegistry retryRegistry = injector.getInstance(RetryRegistry.class);
191+
config.getRetries().forEach((name, retryConfig) -> {
192+
if (retryConfig.getDefaults()) {
193+
retryRegistry.retry(name);
194+
} else {
195+
retryRegistry.retry(name, RetryConfig.custom()
196+
.maxAttempts(retryConfig.getMaxAttempts())
197+
.waitDuration(Duration.ofMillis(retryConfig.getWaitDurationInMillis()))
198+
.build());
199+
}
200+
});
201+
202+
// dropwizard metrics
155203
if (config.isMetrics() && injector.getExistingBinding(Key.get(MetricRegistry.class)) != null) {
156204
MetricRegistry metricRegistry = injector.getInstance(MetricRegistry.class);
157205
metricRegistry.registerAll(injector.getInstance(CircuitBreakerMetrics.class));
158206
metricRegistry.registerAll(injector.getInstance(RateLimiterMetrics.class));
159207
metricRegistry.registerAll(injector.getInstance(RetryMetrics.class));
160208
}
209+
210+
// prometheus
161211
if (config.isPrometheus() && injector.getExistingBinding(Key.get(CollectorRegistry.class)) != null) {
162212
CollectorRegistry collectorRegistry = injector.getInstance(CollectorRegistry.class);
163213
injector.getInstance(CircuitBreakerExports.class).register(collectorRegistry);
164214
injector.getInstance(RateLimiterExports.class).register(collectorRegistry);
165215
}
216+
166217
}
167218

168219
}

0 commit comments

Comments
 (0)