diff --git a/docs/src/main/asciidoc/spring-cloud-circuitbreaker.adoc b/docs/src/main/asciidoc/spring-cloud-circuitbreaker.adoc new file mode 100755 index 00000000..e1d3c4a3 --- /dev/null +++ b/docs/src/main/asciidoc/spring-cloud-circuitbreaker.adoc @@ -0,0 +1,82 @@ +== Introduction + +Spring Cloud Circuit breaker provides an abstraction across different circuit breaker implementations. +It provides a consistent API to use in your applications allowing you the developer to choose the circuit breaker implementation that best fits your needs for your app. + +=== Supported Implementations + +* https://github.com/Netflix/Hystrix[Netfix Hystrix] +* https://github.com/resilience4j/resilience4j[Resilience4J] +* https://github.com/alibaba/Sentinel[Sentinel] +* https://github.com/spring-projects/spring-retry[Spring Retry] + +== Core Concepts + +To create a circuit breaker in your code you can use the `CircuitBreakerFactory` API. When you include a Spring Cloud Circuit Breaker starter on your classpath a bean implementing this API will automatically be created for you. +A very simple example of using this API is given below + +==== +[source,java] +---- +@Service +public static class DemoControllerService { + private RestTemplate rest; + private CircuitBreakerFactory cbFactory; + + public DemoControllerService(RestTemplate rest, CircuitBreakerFactory cbFactory) { + this.rest = rest; + this.cbFactory = cbFactory; + } + + public String slow() { + return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback"); + } + +} +---- +==== + +The `CircuitBreakerFactory.create` API will create an instance of a class called `CircuitBreaker`. +The `run` method takes a `Supplier` and a `Function`. +The `Supplier` is the code that you are going to wrap in a circuit breaker. +The `Function` is the fallback that will be executed if the circuit breaker is tripped. +The function will be passed the `Throwable` that caused the fallback to be triggered. +You can optionally exclude the fallback if you do not want to provide one. + +=== Circuit Breakers In Reactive Code + +If Project Reactor is on the class path then you can also use `ReactiveCircuitBreakerFactory` for your reactive code. + +==== +[source,java] +---- +@Service +public static class DemoControllerService { + private ReactiveCircuitBreakerFactory cbFactory; + private WebClient webClient; + + + public DemoControllerService(WebClient webClient, ReactiveCircuitBreakerFactory cbFactory) { + this.webClient = webClient; + this.cbFactory = cbFactory; + } + + public Mono slow() { + return webClient.get().uri("/slow").retrieve().bodyToMono(String.class).transform( + it -> cbFactory.create("slow").run(it, throwable -> return Mono.just("fallback"))); + } +} +---- +==== + +The `ReactiveCircuitBreakerFactory.create` API will create an instance of a class called `ReactiveCircuitBreaker`. +The `run` method takes with a `Mono` or `Flux` and wraps it in a circuit breaker. +You can optionally profile a fallback `Function` which will be called if the circuit breaker is tripped and will be passed the `Throwable` +that caused the failure. + +== Configuration + +You can configure your circuit breakers using by creating beans of type `Customizer`. +The `Customizer` interface has a single method called `customize` that takes in the `Object` to customize. + +// TODO link to implementation docs \ No newline at end of file diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc index de9f9f32..2d176f94 100644 --- a/docs/src/main/asciidoc/spring-cloud-commons.adoc +++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc @@ -767,4 +767,8 @@ If you want to learn more about the Spring Cloud Release train compatibility, yo In order to disable this feature, set `spring.cloud.compatibility-verifier.enabled` to `false`. If you want to override the compatible Spring Boot versions, just set the `spring.cloud.compatibility-verifier.compatible-boot-versions` property with a comma separated list -of compatible Spring Boot versions. \ No newline at end of file +of compatible Spring Boot versions. + +== Spring Cloud Circuit Breaker + +include::spring-cloud-circuitbreaker.adoc[] \ No newline at end of file diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/AbstractCircuitBreakerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/AbstractCircuitBreakerFactory.java new file mode 100644 index 00000000..1b97ff7c --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/AbstractCircuitBreakerFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Base class for factories which produce circuit breakers. + * + * @author Ryan Baxter + */ +public abstract class AbstractCircuitBreakerFactory> { + + private final ConcurrentHashMap configurations = new ConcurrentHashMap<>(); + + /** + * Adds configurations for circuit breakers. + * @param ids The id of the circuit breaker + * @param consumer A configuration builder consumer, allows consumers to customize the + * builder before the configuration is built + */ + public void configure(Consumer consumer, String... ids) { + for (String id : ids) { + CONFB builder = configBuilder(id); + consumer.accept(builder); + CONF conf = builder.build(); + getConfigurations().put(id, conf); + } + } + + /** + * Gets the configurations for the circuit breakers. + * @return The configurations + */ + protected ConcurrentHashMap getConfigurations() { + return configurations; + } + + /** + * Creates a configuration builder for the given id. + * @param id The id of the circuit breaker + * @return The configuration builder + */ + protected abstract CONFB configBuilder(String id); + + /** + * Sets the default configuration for circuit breakers. + * @param defaultConfiguration A function that returns the default configuration + */ + public abstract void configureDefault(Function defaultConfiguration); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/CircuitBreaker.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/CircuitBreaker.java new file mode 100644 index 00000000..6e98628d --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/CircuitBreaker.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Spring Cloud circuit breaker. + * + * @author Ryan Baxter + */ +public interface CircuitBreaker { + + default T run(Supplier toRun) { + return run(toRun, throwable -> { + throw new NoFallbackAvailableException("No fallback available.", throwable); + }); + }; + + T run(Supplier toRun, Function fallback); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/CircuitBreakerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/CircuitBreakerFactory.java new file mode 100644 index 00000000..ea4f20cc --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/CircuitBreakerFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +/** + * Creates circuit breakers based on the underlying implementation. + * + * @author Ryan Baxter + */ +public abstract class CircuitBreakerFactory> + extends AbstractCircuitBreakerFactory { + + public abstract CircuitBreaker create(String id); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ConfigBuilder.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ConfigBuilder.java new file mode 100644 index 00000000..4c0e021d --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ConfigBuilder.java @@ -0,0 +1,28 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +/** + * A builder for circuit breaker configurations. + * + * @author Ryan Baxter + */ +public interface ConfigBuilder { + + CONF build(); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/Customizer.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/Customizer.java new file mode 100644 index 00000000..bda6d7b1 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/Customizer.java @@ -0,0 +1,28 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +/** + * Customizes the parameterized class. + * + * @author Ryan Baxter + */ +public interface Customizer { + + void customize(TOCUSTOMIZE tocustomize); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/NoFallbackAvailableException.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/NoFallbackAvailableException.java new file mode 100644 index 00000000..9899c40c --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/NoFallbackAvailableException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +/** + * A runtime exception that tells no fallback is available for the circuit breaker. + * + * @author Toshiaki Maki + */ +public class NoFallbackAvailableException extends RuntimeException { + + public NoFallbackAvailableException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ReactiveCircuitBreaker.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ReactiveCircuitBreaker.java new file mode 100644 index 00000000..77b654ef --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ReactiveCircuitBreaker.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +import java.util.function.Function; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Spring Cloud reactive circuit breaker API. + * + * @author Ryan Baxter + */ +public interface ReactiveCircuitBreaker { + + default Mono run(Mono toRun) { + return run(toRun, throwable -> { + throw new NoFallbackAvailableException("No fallback available.", throwable); + }); + } + + Mono run(Mono toRun, Function> fallback); + + default Flux run(Flux toRun) { + return run(toRun, throwable -> { + throw new NoFallbackAvailableException("No fallback available.", throwable); + }); + } + + Flux run(Flux toRun, Function> fallback); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ReactiveCircuitBreakerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ReactiveCircuitBreakerFactory.java new file mode 100644 index 00000000..19222a18 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/circuitbreaker/ReactiveCircuitBreakerFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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 org.springframework.cloud.client.circuitbreaker; + +/** + * Creates reactive circuit breakers. + * + * @author Ryan Baxter + */ +public abstract class ReactiveCircuitBreakerFactory> + extends AbstractCircuitBreakerFactory { + + public abstract ReactiveCircuitBreaker create(String id); + +}