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'