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.