0

I have a need to save Spring Cloud Gateway routes within a database and to do this we send a web request using the WebClient to another microservice. I'm using Eureka for service discovery and want the WebClient to use discovery instance names instead of explicit URLs and I've therefore utilised the @LoadBalanced annotation on the bean method:

    @Bean
    public WebClient loadBalancedWebClientBuilder(WebClient.Builder builder) {
        return builder
                .exchangeStrategies(exchangeStrategies())
                .build();
    }

    @Bean
    @LoadBalanced
    WebClient.Builder builder() {
        return WebClient.builder();
    }

    private ExchangeStrategies exchangeStrategies() {
        return ExchangeStrategies.builder()
                .codecs(clientCodecConfigurer -> {
                    clientCodecConfigurer.defaultCodecs().jackson2JsonEncoder(getEncoder());
                    clientCodecConfigurer.defaultCodecs().jackson2JsonDecoder(getDecoder());
                }).build();
    }

This all works on start-up and for the default 35s cache time - i.e. the webClient discovers the required 'saveToDatabase' service instance and sends the request.

On each eventPublisher.publishEvent(new RefreshRoutesEvent(this)) a call is made to the same downstream microservice (via the WebClient) to retrieve all saved routes. Again this works initially, but after the default 35seconds the load balancer cache seems to be cleared and the downstream service id can no longer be found:

WARN  o.s.c.l.core.RoundRobinLoadBalancer - No servers available for service: QUERY-SERVICE

I have confirmed it is the cache refresh purging the cache and not re-acquiring the instances by setting

spring:
  application:
    name: my-gateway
  cloud:
    loadbalancer:
      cache:
        enabled: true
        ttl: 240s
      health-check:
        refetch-instances: true
      ribbon:
        enabled: false
    gateway:
       ...

I've struggled with this for days now and cannot find/ see where or why the cache is not being updated, only purged. Adding specific @LoadBalancerClient() configuration as below makes no difference.

    @Bean
    public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                .withDiscoveryClient()
                .withHealthChecks()
                .withCaching()
                .withRetryAwareness()
                .build(context);
    }

Clearly this must work for other people, so what am I missing?!

Thanks.

Mike at Savient
  • 250
  • 2
  • 12
  • We'd need a proper sample that reproduces the issue to verify this. Feel free to create a GitHub issue in spring-cloud-commons with a link to such a sample repo. – OlgaMaciaszek Feb 14 '23 at 15:43
  • Okay, thanks. I believe what has been happening is that within the WebClient reactive stream an error was thrown but not trapped correctly. When the error happens, the service instance is removed from the cache and doesn't come back. Would this be 'by design' or not? – Mike at Savient Feb 15 '23 at 17:08
  • 1
    As written in the docs (https://docs.spring.io/spring-cloud-commons/docs/4.0.2-SNAPSHOT/reference/html/#instance-health-check-for-loadbalancer), since HealthCheckServiceInstanceListSupplier has its own caching mechanism, we recommend disabling the global LB cache for the clients that use it. – OlgaMaciaszek Feb 20 '23 at 12:05

0 Answers0