java, spring,

Code Spring Cloud Gateway Circuitbreaker

Cui Cui Follow Oct 05, 2022 · 4 mins read
Code Spring Cloud Gateway Circuitbreaker
Share this

“I paint with a brayer and press. A color lover with monochrome moods”. -Kathleen DeMeo

Dependency

Maven POM

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
        </dependency>

Is this gonna work?

Option A - Spring YAML Configuration

# resiliency4j dependency's properties configuration
resilience4j:
  circuitbreaker:
    instances:
      circuitBreakerService:
        slidingWindowSize: 10                     # examine every last 10 requests in closed state
        permittedNumberOfCallsInHalfOpenState: 5  # examine every last 5 requests in half open state
        failureRateThreshold: 50                  # 50% requests failed, will change to open state
        waitDurationInOpenState: 10000            # circuit becomes half open after 10 seconds
        registerHealthIndicator: true
  timelimiter:
    instances:
      circuitBreakerService:
        timeoutDuration: 10s

# cloud gateway's properties configuration
spring:
  cloud:
    gateway:
      routes:
        - id: yaml_route_id
          uri: ${properties.upstream-service-url}
          predicates:
            - Method=POST,PATCH
            - Path=/v2/api/resources,/v2/api/resources/{id}
            - ReadBodyToString=String.class
          filters:
            - UpstreamServiceApiFilter
            - name: Retry
              args:
                methods:
                  - POST
                  - PATCH
                retries: ${properties.retry-times}
                backoff:
                  firstBackoff: ${properties.retry-first-backoff}
                  maxBackoff: ${properties.retry-max-backoff}
            - name: CircuitBreaker
              args:
                name: circuitBreakerService
                fallbackUri: forward:/circuit-breaker-fallback

# custom properties
properties:
    upstream-service-url: http://upstream-api
    retry-times: 3
    retry-first-backoff: 50ms
    retry-max-backoff: 500ms

Option B - Spring Java Bean Configuration

    private final ApplicationProperties properties;
    private final UpstreamServiceApiFilter upstreamServiceApiFilter;

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("java_route_id", r -> r.path("/v2/api/resources", "/v2/api/resources/{id}").and().method("POST", "PATCH")
                        .filters(f -> f.modifyRequestBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE,
                                (exchange, body) -> {
                                    // Do whatever you want with the request body here. 
                                })
                                .filter(upstreamServiceApiFilter.apply(new UpstreamServiceApiFilter.Config()))
                                .retry(config -> {
                                    var backoffConfig = new RetryGatewayFilterFactory.BackoffConfig();
                                    backoffConfig.setFirstBackoff(properties.getRetryFirstBackoff());
                                    backoffConfig.setMaxBackoff(properties.getRetryMaxBackoff());
                                    config.setMethods(HttpMethod.POST, HttpMethod.PATCH)
                                        .setRetries(properties.getRetryTimes())
                                        .setBackoff(backoffConfig);
                                })
                                .circuitBreaker(config -> config.setName("circuitBreakerService").setFallbackUri("forward:/circuit-breaker-fallback"))
                        ).uri(properties.getUpstreamServiceUrl())
                )
                .build();
    }

Spring Cloud Gateway Filter

@Slf4j
@Component
public class UpstreamServiceApiFilter extends AbstractGatewayFilterFactory<UpstreamServiceApiFilter.Config> {

    @Override
    public GatewayFilter apply(UpstreamServiceApiFilter.Config config) {
        return (exchange, chain) -> {
            
            // Do your business logic for the filter

            return chain.filter(exchange).doFinally(signalType -> {
                HttpStatus httpStatus = exchange.getResponse().getStatusCode();
                if (!httpStatus.is2xxSuccessful()) {
                    log.warn("Got this response code: {}", httpStatus.value());
                    // Do final process if it's bad response from upstream services
                }
            });
        };
    }

    public static class Config {
        // Put the configuration properties
    }
}

Spring Cloud Gateway fallback controller

@RestController
@AllArgsConstructor
public class FallbackController {
    private final ObjectMapper objectMapper;
    
    @RequestMapping(value = "/circuit-breaker-fallback", method = {GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE})
    Flux<String> getFallback(ServerHttpResponse response) throws JsonProcessingException {
        response.setStatusCode(SERVICE_UNAVAILABLE);
        return Flux.just(objectMapper.writeValueAsString(Map.of(
                "code", "503.1.123",
                "message", "Customized Circuit Breaker Fallback Message."
        )));
    }
}

Happy coding!

Join Newsletter
Get the latest news right in your inbox. We never spam!
Cui
Written by Cui Follow
Hi, I am Z, the coder for cuizhanming.com!