Skip to content

Commit

Permalink
Issue ReactiveX#74: adding ratpack docs and fixing some configuration…
Browse files Browse the repository at this point in the history
… issues

* adding docs and fixing some configuration issues, closes ReactiveX#74

* removing unused import
  • Loading branch information
drmaas authored and RobWin committed Apr 1, 2017
1 parent 987c3f7 commit a8b7101
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 106 deletions.
165 changes: 165 additions & 0 deletions resilience4j-ratpack/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
= resilience4j-ratpack

== Gradle

```
repositories {
maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' }
mavenCentral()
}


dependencies {
compile('io.github.resilience4j:resilience4j-ratpack:0.9.0-SNAPSHOT')
}
```

== Promises

Ratpack promises provide the means by which an application can become fully non-blocking and asynchronous.
Resilience4j provides transformers that can be applied to Promises. This is ideal when promising a value
that is coming from some sort of I/O source.

=== CircuitBreaker

You can easily apply a CircuitBreaker to any Ratpack Promise.

[source,java]
----
public Promise<String> methodWhichReturnsAPromise() {
return backendBConnector.methodWhichReturnsAPromise()
.transform(CircuitBreakerTransformer.of(circuitBreaker).recover(t -> "recovered"));
}
----

=== Retry

You can easily apply a Retry to any Ratpack Promise.

[source,java]
----
public Promise<String> methodWhichReturnsAPromise() {
return backendBConnector.methodWhichReturnsAPromise()
.transform(RetryTransformer.of(retry).recover(t -> "recovered"));
}
----

=== RateLimiter

You can easily apply a RateLimiter to any Ratpack Promise.

[source,java]
----
public Promise<String> methodWhichReturnsAPromise() {
return backendBConnector.methodWhichReturnsAPromise()
.transform(RateLimiterTransformer.of(rateLimiter).recover(t -> "recovered"));
}
----

== Guice AOP

Guice provides method interception capabilities. Here are provided some annotations which allow
methods returning promises, completion stage, and normal values, to be operated on.

=== CircuitBreaker
The demo shows how to use the `CircuitBreaker` annotation to make your Ratpack application more fault tolerant.
You can either annotate a class in order to protect all public methods or just some specific methods.
For example:

[source,java]
----
@CircuitBreaker(name = "backendA", recovery = MyRecoveryFunction.class)
@Singleton
public class BackendAConnector implements Connector {
...
}
----
Where `MyRecoveryFunction` is implements `io.github.resilience4j.ratpack.RecoveryFunction` and provides
a fallback value that is returned when the circuit breaker identified by `name` is open.

=== Retry
TODO
Retry will be supported once the concept of a RetryRegistry is in place.

=== RateLimiter
The demo shows how to use the `RateLimiter` annotation to make your Ratpack application more fault tolerant.
You can either annotate a class in order to protect all public methods or just some specific methods.
For example:

[source,java]
----
@RateLimiter(name = "backendA", recovery = MyRecoveryFunction.class)
@Singleton
public class BackendAConnector implements Connector {
...
}
----
Where `MyRecoveryFunction` is implements `io.github.resilience4j.ratpack.RecoveryFunction` and provides
a fallback value that is returned when the rate limiter rate limit identified by `name` is exceeded.

== Functional style

You can still use a functional programming style for CircuitBreaker, Retry, and RateLimiter. For example:

[source,java]
----
@Singleton
public class BusinessBService implements BusinessService {
public Try<String> methodWithRecovery() {
Try.CheckedSupplier<String> backendFunction = CircuitBreaker.decorateCheckedSupplier(circuitBreaker, () -> backendBConnector.failure());
return Try.of(backendFunction)
.recover((throwable) -> recovery(throwable));
}
private String recovery(Throwable throwable) {
// Handle exception and invoke fallback
return "Hello world from recovery";
}
}
----

== Monitoring
TODO
Ratpack provides the concept of a health check. See https://ratpack.io/manual/current/api/ratpack/health/HealthCheckHandler.html for details.
CircuitBreaker, RateLimiter, and Retry health checks have not been implemented yet.
When implemented they should function similarly to the spring boot health checks.

== Configuration

You must install the `ResilienceModule` if you want to use AOP so that the method interceptors work.
You can optionally configure your CircuitBreakers, RateLimiters, and Retries in your Guice Module as well.

Note: If you don't register a `CircuitBreakerRegistry` or `RateLimiterRegistry`, the defaults
will be used when using AOP.

For example

[source,java]
----
public class MyModule extends AbstractModule {
@Override
protected void configure() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom();
bind(CircuitBreakerRegistry.class).toInstance(CircuitBreakerRegistry.of(config);
install(ResilienceModule.class);
}
}
----

== CircuitBreaker Event Monitoring
TODO
Handlers for displaying the last X CircuitBreaker, Ratelimiter, or Retry events are not yet implemented.
When implemented they should function similarly to the spring boot management endpoints.

== License

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.
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,24 @@
*/
package io.github.resilience4j.ratpack;

import com.google.inject.AbstractModule;
import com.google.inject.matcher.Matchers;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.github.resilience4j.ratpack.annotation.CircuitBreaker;
import io.github.resilience4j.ratpack.annotation.RateLimiter;
import io.github.resilience4j.ratpack.internal.CircuitBreakerMethodInterceptor;
import io.github.resilience4j.ratpack.internal.RateLimiterMethodInterceptor;
import ratpack.guice.ConfigurableModule;

public class ResilienceModule extends ConfigurableModule<ResilienceModule.ResilienceConfig> {
public class ResilienceModule extends AbstractModule {

@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(CircuitBreaker.class), injected(new CircuitBreakerMethodInterceptor(getProvider(CircuitBreakerRegistry.class))));
bindInterceptor(Matchers.any(), Matchers.annotatedWith(RateLimiter.class), injected(new RateLimiterMethodInterceptor(getProvider(RateLimiterRegistry.class))));
bindInterceptor(Matchers.any(), Matchers.annotatedWith(CircuitBreaker.class), injected(new CircuitBreakerMethodInterceptor()));
bindInterceptor(Matchers.any(), Matchers.annotatedWith(RateLimiter.class), injected(new RateLimiterMethodInterceptor()));
}

private <T> T injected(T instance) {
requestInjection(instance);
return instance;
}

public static class ResilienceConfig {
private boolean enableMetrics = false;

public ResilienceConfig enableMetrics(boolean enableMetrics) {
this.enableMetrics = enableMetrics;
return this;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package io.github.resilience4j.ratpack.internal;

import com.google.inject.Provider;
import com.google.inject.Inject;
import io.github.resilience4j.circuitbreaker.CircuitBreakerOpenException;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.circuitbreaker.operator.CircuitBreakerOperator;
Expand All @@ -29,7 +29,6 @@
import org.aopalliance.intercept.MethodInvocation;
import ratpack.exec.Promise;

import javax.inject.Inject;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
Expand All @@ -41,18 +40,20 @@
*/
public class CircuitBreakerMethodInterceptor implements MethodInterceptor {

private final Provider<CircuitBreakerRegistry> provider;
@Inject(optional = true)
private CircuitBreakerRegistry registry;

@Inject
public CircuitBreakerMethodInterceptor(Provider<CircuitBreakerRegistry> provider) {
this.provider = provider;
public CircuitBreakerMethodInterceptor() {
if (registry == null) {
registry = CircuitBreakerRegistry.ofDefaults();
}
}

@SuppressWarnings("unchecked")
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
CircuitBreaker annotation = invocation.getMethod().getAnnotation(CircuitBreaker.class);
io.github.resilience4j.circuitbreaker.CircuitBreaker breaker = provider.get().circuitBreaker(annotation.name());
io.github.resilience4j.circuitbreaker.CircuitBreaker breaker = registry.circuitBreaker(annotation.name());
if (breaker == null) {
return invocation.proceed();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,17 @@

package io.github.resilience4j.ratpack.internal;

import com.google.inject.Provider;

import com.google.inject.Inject;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
import io.github.resilience4j.ratpack.annotation.RateLimiter;
import io.github.resilience4j.ratpack.RateLimiterTransformer;
import io.github.resilience4j.ratpack.RecoveryFunction;
import io.github.resilience4j.ratpack.annotation.RateLimiter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import ratpack.exec.Promise;

import javax.inject.Inject;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
Expand All @@ -40,19 +38,21 @@
*/
public class RateLimiterMethodInterceptor implements MethodInterceptor {

private final Provider<RateLimiterRegistry> provider;
@Inject(optional = true)
private RateLimiterRegistry registry;

@Inject
public RateLimiterMethodInterceptor(Provider<RateLimiterRegistry> provider) {
this.provider = provider;
public RateLimiterMethodInterceptor() {
if (registry == null) {
registry = RateLimiterRegistry.of(RateLimiterConfig.ofDefaults());
}
}

@SuppressWarnings("unchecked")
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
RateLimiter annotation = invocation.getMethod().getAnnotation(RateLimiter.class);
RecoveryFunction<?> recoveryFunction = annotation.recovery().newInstance();
io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = provider.get().rateLimiter(annotation.name());
io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = registry.rateLimiter(annotation.name());
if (rateLimiter == null) {
return invocation.proceed();
}
Expand Down

This file was deleted.

Loading

0 comments on commit a8b7101

Please sign in to comment.