Spring Cloud Gateway Resilience4j specific Circuit Breaker assigned to a route

Viewed 807

Spring Cloud 2020.0.3

Attempting to configure reactive circuit breakers per defined route. However, I always get the default circuit breaker properties rather than the specific auth-service instance properties.

POM:

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.3</spring-cloud.version>
        <jersey-apache-client4.ver>1.19.4</jersey-apache-client4.ver>
        <resilience4j-spring-boot2.ver>1.7.1</resilience4j-spring-boot2.ver>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Circuit Breaker -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <!--<version>${resilience4j-spring-boot2.ver}</version>-->
        </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-webflux</artifactId>
        </dependency>
        <!-- Needed for AbastractDiscoveryClientOptionalArgs -->
        <dependency>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-apache-client4</artifactId>
            <version>${jersey-apache-client4.ver}</version>
        </dependency>
        <!-- UTILITIES -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- TESTING -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </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>

application.yml

server: 
  port: 8765

spring:
  application:
    name: api-gateway
  #config first. Configuration Server not registered in cloud
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config-server
      profile: default
      label: latest
      fail-fast: true
    circuitbreaker:
      resilience4j:
        enabled: true
  main:
    banner-mode: off
eureka:
  instance:
    securePortEnabled: false
    nonSecurePortEnabled: true
    trust-all-certs: true
    trust-all-hostnames: true
  client:
    enabled: true
    register-with-eureka: true
    fetch-registry: true
    serviceUrl:
      defaultZone: http://${hostname}:8761/eureka

#https://github.com/Romeh/spring-cloud-gateway-resilience4j
resilience4j.circuitbreaker:
  configs:
    default:
      slidingWindowSize: 10
      minimumNumberOfCalls: 5
      permittedNumberOfCallsInHalfOpenState: 3
      automaticTransitionFromOpenToHalfOpenEnabled: true
      waitDurationInOpenState: 2s
      failureRateThreshold: 5
      eventConsumerBufferSize: 10
      slowCallDurationThreshold: 200ms
      slowCallRateThreshold: 30
      recordExceptions:
        - org.springframework.web.client.HttpServerErrorException
        - java.io.IOException
      ignoreExceptions:
        - java.lang.IllegalStateException
    shared:
      slidingWindowSize: 100
      permittedNumberOfCallsInHalfOpenState: 30
      waitDurationInOpenState: 1s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      ignoreExceptions:
        - java.lang.IllegalStateException
    instances:
      auth-service:
        slidingWindowSize: 1
        minimumNumberOfCalls: 10
        permittedNumberOfCallsInHalfOpenState: 3
        waitDurationInOpenState: 1s
        failureRateThreshold: 3
        eventConsumerBufferSize: 1

resilience4j.timelimiter:
  time-limiter-aspect-order: 398
  configs:
    default:
      timeoutDuration: 1s
      cancelRunningFuture: false
  instances:
    auth-service:
      timeoutDuration: 250ms

Router:

@Bean
public RouteLocator gatewayRouter(RouteLocatorBuilder builder) {
    log.debug(PATH_PREFIX + CloudPath.AUTH_PATH.getPath() + ALL_SUBPATHS);
    Collection<GatewayFilter> filters = new ArrayList<>();
    filters.add(inspect);
    log.debug("######### gatewayRouter INIT");  //happens before circuit breaker configuration
    return builder.routes()
        .route(p -> p.path(PATH_PREFIX + CloudPath.CONFIG_PATH.getPath() + ALL_SUBPATHS)
        .filters(f -> f.filters(filters)) //pre-filter
        .uri(buildUri(CloudPath.CONFIG_PATH))
        )
        .route(p -> p.path(PATH_PREFIX + CloudPath.AUTH_PATH.getPath() + ALL_SUBPATHS)
        .filters(f -> f.circuitBreaker(c
            -> c.setName(CloudPath.AUTH_PATH.getPath())
                .setFallbackUri(AUTH_SERVICE_FALLBACK_URI)
            )
        )
        .uri(buildUri(CloudPath.AUTH_PATH))
        )
        .build();
}

Custom Authentication route circuit breaker factory:

@Bean(name = "authCktBrkrFactory")
public ReactiveResilience4JCircuitBreakerFactory authServiceCircuitBreakerFactory(CircuitBreakerRegistry circuitBreakerRegistry, TimeLimiterRegistry timeLimiterRegistry) {

    log.debug("CIRCUIT BREAKER COUNT = " + circuitBreakerRegistry.getAllCircuitBreakers().asJava().size());
    circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(b -> log.debug("BREAKER:: " + b.getName()));
    ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory = new ReactiveResilience4JCircuitBreakerFactory(circuitBreakerRegistry, timeLimiterRegistry);
    // inject the created spring managed bean circuit breaker registry will all externally configured CBs
    reactiveResilience4JCircuitBreakerFactory.configureCircuitBreakerRegistry(circuitBreakerRegistry);
    // Inject the the created spring managed bean time limter config for specific backend name otherwise use the default configuration from resilience4j
    reactiveResilience4JCircuitBreakerFactory.configure(
        builder -> builder
            .timeLimiterConfig(timeLimiterRegistry.getConfiguration(CloudPath.AUTH_PATH.getPath()).orElse(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(300)).build()))
            .circuitBreakerConfig(circuitBreakerRegistry.getConfiguration(CloudPath.AUTH_PATH.getPath()).orElse(circuitBreakerRegistry.getDefaultConfig())), CloudPath.AUTH_PATH.getPath()
    );
    logConfig(reactiveResilience4JCircuitBreakerFactory.getCircuitBreakerRegistry().circuitBreaker(CloudPath.AUTH_PATH.getPath()));
    return reactiveResilience4JCircuitBreakerFactory;
}

private void logConfig(CircuitBreaker cb) {
    CircuitBreakerConfig cbc = cb.getCircuitBreakerConfig();
    StringBuilder msg = new StringBuilder();
    msg.append("\n").append(cb.getName());
    msg.append("\n\t State = ").append(cb.getState().name());
    msg.append("\n\t ").append(cbc.toString().replace("{", "\n\t{\n\t  ").replaceAll(",", "\n\t,").replace("}", "\n\t}\n"));
    log.debug(msg.toString());
}

LOGS:

2021-11-08 13:46:02,119 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'gatewayRouter'

2021-11-08 13:46:02,120 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'routeLocatorBuilder'

2021-11-08 13:46:02,121 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'org.springframework.cloud.gateway.config.GatewayAutoConfiguration'

2021-11-08 13:46:02,122 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'routeLocatorBuilder' via factory method to bean named 'org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext@61f7b9e8'

2021-11-08 13:46:02,123 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'gatewayRouter' via factory method to bean named 'routeLocatorBuilder'

2021-11-08 13:46:02,126 DEBUG [restartedMain] com.my.gateway.config.ApiGatewayRoutes.gatewayRouter:: /auth-service/**

2021-11-08 13:46:02,126 DEBUG [restartedMain] com.my.gateway.config.ApiGatewayRoutes.gatewayRouter:: ######### gatewayRouter INIT

2021-11-08 13:46:02,132 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'pathRoutePredicateFactory'

2021-11-08 13:46:02,151 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'springCloudCircuitBreakerResilience4JFilterFactory'

021-11-08 13:46:02,151 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration'

2021-11-08 13:46:02,152 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'authCktBrkrFactory'

2021-11-08 13:46:02,153 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'circuitBreakerRegistry'

2021-11-08 13:46:02,154 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerConfigurationOnMissingBean'

2021-11-08 13:46:02,155 DEBUG [restartedMain] org.springframework.core.LocalVariableTableParameterNameDiscoverer.inspectClass:: Cannot find '.class' file for class [class io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerConfigurationOnMissingBean$$EnhancerBySpringCGLIB$$f70f2830] - unable to determine constructor/method parameter names

2021-11-08 13:46:02,156 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'resilience4j.circuitbreaker-io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerProperties'

2021-11-08 13:46:02,236 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerConfigurationOnMissingBean' via constructor to bean named 'resilience4j.circuitbreaker-io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerProperties'

2021-11-08 13:46:02,239 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'eventConsumerRegistry'

2021-11-08 13:46:02,253 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'circuitBreakerRegistryEventConsumer'

2021-11-08 13:46:02,254 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'taggedCircuitBreakerMetricsPublisher'

2021-11-08 13:46:02,254 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerMetricsAutoConfiguration'

2021-11-08 13:46:02,255 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'taggedCircuitBreakerMetricsPublisher' via factory method to bean named 'simpleMeterRegistry'

2021-11-08 13:46:02,267 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'compositeCircuitBreakerCustomizer'

2021-11-08 13:46:02,269 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'circuitBreakerRegistry' via factory method to bean named 'eventConsumerRegistry'

2021-11-08 13:46:02,269 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'circuitBreakerRegistry' via factory method to bean named 'circuitBreakerRegistryEventConsumer'

2021-11-08 13:46:02,269 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'circuitBreakerRegistry' via factory method to bean named 'compositeCircuitBreakerCustomizer'

2021-11-08 13:46:02,293 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'timeLimiterRegistry'

2021-11-08 13:46:02,293 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'io.github.resilience4j.timelimiter.autoconfigure.TimeLimiterConfigurationOnMissingBean'

2021-11-08 13:46:02,295 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'resilience4j.timelimiter-io.github.resilience4j.timelimiter.autoconfigure.TimeLimiterProperties'

2021-11-08 13:46:02,302 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'timeLimiterEventsConsumerRegistry'

2021-11-08 13:46:02,306 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'timeLimiterRegistryEventConsumer'

2021-11-08 13:46:02,307 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'taggedTimeLimiterMetricsPublisher'

2021-11-08 13:46:02,307 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'io.github.resilience4j.timelimiter.autoconfigure.TimeLimiterMetricsAutoConfiguration'

2021-11-08 13:46:02,308 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'taggedTimeLimiterMetricsPublisher' via factory method to bean named 'simpleMeterRegistry'

2021-11-08 13:46:02,314 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'compositeTimeLimiterCustomizer'

2021-11-08 13:46:02,316 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'timeLimiterRegistry' via factory method to bean named 'resilience4j.timelimiter-io.github.resilience4j.timelimiter.autoconfigure.TimeLimiterProperties'

2021-11-08 13:46:02,317 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'timeLimiterRegistry' via factory method to bean named 'timeLimiterEventsConsumerRegistry'

2021-11-08 13:46:02,317 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'timeLimiterRegistry' via factory method to bean named 'timeLimiterRegistryEventConsumer'

2021-11-08 13:46:02,317 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'timeLimiterRegistry' via factory method to bean named 'compositeTimeLimiterCustomizer'

2021-11-08 13:46:02,333 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'authCktBrkrFactory' via factory method to bean named 'circuitBreakerRegistry'

2021-11-08 13:46:02,333 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'authCktBrkrFactory' via factory method to bean named 'timeLimiterRegistry'

2021-11-08 13:46:02,345 DEBUG [restartedMain] com.my.gateway.config.GatewayCircuitBreakerConfig.authServiceCircuitBreakerFactory:: CIRCUIT BREAKER COUNT = 0

2021-11-08 13:46:02,361 DEBUG [restartedMain] com.my.gateway.config.GatewayCircuitBreakerConfig.logConfig:: auth-service State = CLOSED CircuitBreakerConfig { recordExceptionPredicate=java.util.function.Predicate$$Lambda$365/341398452@33f6662b , ignoreExceptionPredicate=io.github.resilience4j.core.predicate.PredicateCreator$$Lambda$655/1600196965@40cacc4c , recordExceptions=[class org.springframework.web.client.HttpServerErrorException , class java.io.IOException] , ignoreExceptions=[class java.lang.IllegalStateException] , failureRateThreshold=5.0 , permittedNumberOfCallsInHalfOpenState=3 , slidingWindowSize=10 , slidingWindowType=COUNT_BASED , minimumNumberOfCalls=5 , writableStackTraceEnabled=true , automaticTransitionFromOpenToHalfOpenEnabled=true , waitIntervalFunctionInOpenState=io.github.resilience4j.core.IntervalFunction$$Lambda$649/584419470@33b6cd16 , slowCallRateThreshold=30.0 , slowCallDurationThreshold=PT0.2S }

OF NOTE:

2021-11-08 13:46:02,154 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerConfigurationOnMissingBean'

2021-11-08 13:46:02,155 DEBUG [restartedMain] org.springframework.core.LocalVariableTableParameterNameDiscoverer.inspectClass:: Cannot find '.class' file for class [class io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerConfigurationOnMissingBean$$EnhancerBySpringCGLIB$$f70f2830] - unable to determine constructor/method parameter names

2021-11-08 13:46:02,156 DEBUG [restartedMain] org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton:: Creating shared instance of singleton bean 'resilience4j.circuitbreaker-io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerProperties'

2021-11-08 13:46:02,236 DEBUG [restartedMain] org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray:: Autowiring by type from bean name 'io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerConfigurationOnMissingBean' via constructor to bean named 'resilience4j.circuitbreaker-io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerProperties'



0 Answers
Related