I have implemented a Spring Cloud based application that has the following principal architecture. The GatewayService
delegates to multiple instances of HelloService
. I use Consul for service discovery. GatewayService
is implemented in the following way:
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
@RestController
static class GateWayController {
private final WebClient.Builder webClientBuilder;
@Data
@NoArgsConstructor
private static class Response {
private String message;
private String appName;
private int port;
}
@Autowired
public GateWayController(WebClient.Builder webClientBuilder) {
this.webClientBuilder = webClientBuilder;
}
@GetMapping("/lbhello")
public Mono<Response> hello() {
return webClientBuilder.build()
.get()
.uri("lb://hello-service/hello")
.retrieve()
.bodyToMono(Response.class);
}
}
}
spring-cloud-starter-consul-discovery
and spring-cloud-starter-loadbalancer
areincluded as a dependencies in pom.xml
. In application.yaml
I only set application.name
and disable Ribbon, so that Spring Cloud Loadbalancer is used. When I start the GatewayService
and two instances of HelloService
everything works as expected. Service calls to the lbhello
endpoint are delegated alternately to the two HelloService
instances.
When one instance of HelloService
is stopped, Consul detects that the instance is missing an marks it as critical. As a consequence I would expect that the load balancer stops forwarding requests to the non-existing service, at least after some time. But this never happens. Every second service call is delegated to this service instance and consequently fails. When using Eureka as a discovery service the registry is adapted and only the remaining HelloService
is considered for service calls.
Is this the expected behavior for Consul? If the answer is yes, is there a Spring Cloud setting to adapt this behavior?
The Spring Cloud
ConsulDiscoveryClient
loads all service instance by default, regardless of their health state. This behavior can be changed by the following configuration setting:With this setting request are not delegated to non-healty instances after some delay.