spring-cloud / spring-cloud-circuitbreaker

Spring Cloud Circuit Breaker API and Implementations
Apache License 2.0
328 stars 109 forks source link

CircuitBreaker not working on fresh Spring Boot 2.6.7 project #146

Closed wnederhof closed 2 years ago

wnederhof commented 2 years ago

When I create a brand new Spring Boot project (Spring 2.6.7 with Resilience4J and Spring Web selected), I expect the @CircuitBreaker annotation to call a fallback method in case of an exception when applied on a method, but it does not work.

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.2</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

DemoApplication.java:

package com.example.demo;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpServerErrorException;

@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping
    @CircuitBreaker(name = "myCircuitBreaker", fallbackMethod = "exampleFallback")
    public String example() {
        throw new HttpServerErrorException(HttpStatus.MULTI_STATUS); // The exception thrown here is completely arbitrary.
    }

    public String exampleFallback(HttpServerErrorException e) {
        return "This is what I expect to see.";
    }

}

When running this application and running curl localhost:8080, instead of getting "This is what I expect to see.", I'm getting:

{"timestamp":"2022-04-29T07:21:46.194+00:00","status":500,"error":"Internal Server Error","path":"/"}

Am I using @Resilience4J in the wrong way, or is there an issue with Resilience4J in combination with Spring Boot 2.6.7?

Thank you in advance.

ryanjbaxter commented 2 years ago

I don't see how this is related to spring cloud circuit breaker seems like you are just using vanilla Resilience4J

wnederhof commented 2 years ago

It should be related to this project because it uses spring-cloud-starter-circuitbreaker-resilience4j. I also posted this issue in the resilience4j repository, but they mention they don't own this...

https://github.com/resilience4j/resilience4j/issues/1675#issuecomment-1112958958

The annotation @CircuitBreaker is made available by resilience4j, so I can assume that it may be used by the starter, right?

ryanjbaxter commented 2 years ago

We don't have any annotation so if the annotation is not working I don't see how it's an issue with out code. If you remove spring cloud circuit breaker and use resilienc4j directly can you reproduce the issue?

wnederhof commented 2 years ago

Hmm, I simply assumed that the starter would set up resilience4j including the annotations it provides. The fact that it doesn't is a bit surprising for me. There is an official example provided where it does (or at least, should) work. In this case, resilience4j is initialized with the io.github.resilience4j:resilience4j-spring-boot2 starter instead of the spring-cloud-circuitbreaker variant. Although it uses a different starter, the annotations are provided by the library instead of the starter (and is therefore also included when using the spring-cloud-starter-circuitbreaker-resilience4j. Therefore it seemed fair to assume that the annotations would also work in this starter.

https://github.com/resilience4j/resilience4j-spring-boot2-demo/blob/master/src/main/java/io/github/robwin/service/BackendAService.java

wnederhof commented 2 years ago

It turns to work as expected if spring-boot-starter-aop is provided. Not sure if this starter should then also depend on aop, so that Resilience4J works with the annotations that are provided? But at least this fixes it for me, so I guess this can be closed. Thank you for thinking along :-)

ryanjbaxter commented 2 years ago

Spring cloud circuit break should not include the aop dependency because we don't need it. I bet if you added the resilience4j starter it would work.