Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Handle exception for restTemplate based function. Add default blockin…
…g health-check configuration.
  • Loading branch information
OlgaMaciaszek committed Dec 11, 2020
commit e1cd99a85a0928b513ea018d9a547c929ad9e38a
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

/**
* @author Spencer Gibb
Expand Down Expand Up @@ -90,7 +92,7 @@ public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceL
}

@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnBean({ ReactiveDiscoveryClient.class, WebClient.Builder.class })
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "health-check")
public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceListSupplier(
Expand Down Expand Up @@ -148,12 +150,12 @@ public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceL
}

@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnBean({ DiscoveryClient.class, RestTemplate.class })
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "health-check")
public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withHealthChecks()
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withBlockingHealthChecks()
.build(context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@

/**
* A {@link ServiceInstanceListSupplier} implementation that verifies whether the
* instances are alive and only returns the healthy one, unless there are none. Uses
* a user-provided function to ping the <code>health</code> endpoint of the instances.
* instances are alive and only returns the healthy one, unless there are none. Uses a
* user-provided function to ping the <code>health</code> endpoint of the instances.
*
* @author Olga Maciaszek-Sharma
* @author Roman Matiushchenko
Expand All @@ -45,8 +45,7 @@
public class HealthCheckServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier
implements InitializingBean, DisposableBean {

private static final Log LOG = LogFactory
.getLog(HealthCheckServiceInstanceListSupplier.class);
private static final Log LOG = LogFactory.getLog(HealthCheckServiceInstanceListSupplier.class);

private final LoadBalancerProperties.HealthCheck healthCheck;

Expand All @@ -62,20 +61,17 @@ public HealthCheckServiceInstanceListSupplier(ServiceInstanceListSupplier delega
LoadBalancerProperties.HealthCheck healthCheck,
BiFunction<ServiceInstance, String, Mono<Boolean>> aliveFunction) {
super(delegate);
defaultHealthCheckPath = healthCheck.getPath()
.getOrDefault("default", "/actuator/health");
defaultHealthCheckPath = healthCheck.getPath().getOrDefault("default", "/actuator/health");
this.aliveFunction = aliveFunction;
this.healthCheck = healthCheck;
Repeat<Object> aliveInstancesReplayRepeat = Repeat
.onlyIf(repeatContext -> this.healthCheck.getRefetchInstances())
.fixedBackoff(healthCheck.getRefetchInstancesInterval());
Flux<List<ServiceInstance>> aliveInstancesFlux = Flux.defer(delegate)
.switchMap(serviceInstances -> healthCheckFlux(serviceInstances)
.map(alive -> Collections
.unmodifiableList(new ArrayList<>(alive))))
.map(alive -> Collections.unmodifiableList(new ArrayList<>(alive))))
.repeatWhen(aliveInstancesReplayRepeat);
aliveInstancesReplay = aliveInstancesFlux
.delaySubscription(healthCheck.getInitialDelay()).replay(1)
aliveInstancesReplay = aliveInstancesFlux.delaySubscription(healthCheck.getInitialDelay()).replay(1)
.refCount(1);
}

Expand Down Expand Up @@ -130,8 +126,7 @@ public Flux<List<ServiceInstance>> get() {
}

protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
String healthCheckPropertyValue = healthCheck.getPath()
.get(serviceInstance.getServiceId());
String healthCheckPropertyValue = healthCheck.getPath().get(serviceInstance.getServiceId());
String healthCheckPath = healthCheckPropertyValue != null ? healthCheckPropertyValue : defaultHealthCheckPath;
return aliveFunction.apply(serviceInstance, healthCheckPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,18 +270,17 @@ private ServiceInstanceListSupplier healthCheckServiceInstanceListSupplier(WebCl
.thenReturn(HttpStatus.OK.value() == clientResponse.rawStatusCode())));
}


private ServiceInstanceListSupplier blockingHealthCheckServiceInstanceListSupplier(RestTemplate restTemplate,
ServiceInstanceListSupplier delegate, LoadBalancerProperties properties) {
return new HealthCheckServiceInstanceListSupplier(delegate, properties.getHealthCheck(),
(serviceInstance, healthCheckPath) -> Mono.defer(() -> {
URI uri = UriComponentsBuilder.fromUri(serviceInstance.getUri()).path(healthCheckPath).build()
.toUri();
try {
return Mono.just(HttpStatus.OK
.equals(restTemplate.getForEntity(uri, Void.class)
.getStatusCode()));
} catch (Exception ignored){
return Mono
.just(HttpStatus.OK.equals(restTemplate.getForEntity(uri, Void.class).getStatusCode()));
}
catch (Exception ignored) {
return Mono.just(false);
}
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

import static org.assertj.core.api.BDDAssertions.then;
Expand Down Expand Up @@ -170,6 +171,17 @@ void shouldNotWrapWithRetryAwareSupplierWhenRetryTemplateOnClasspath() {

}

@Test
void shouldInstantiateBlockingHealthCheckServiceInstanceListSupplier() {
blockingDiscoveryClientRunner.withUserConfiguration(RestTemplateTestConfig.class)
.withPropertyValues("spring.cloud.loadbalancer.configurations=health-check").run(context -> {
ServiceInstanceListSupplier supplier = context.getBean(ServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(HealthCheckServiceInstanceListSupplier.class);
then(((DelegatingServiceInstanceListSupplier) supplier).getDelegate())
.isInstanceOf(DiscoveryClientServiceInstanceListSupplier.class);
});
}

@Configuration
protected static class TestConfig {

Expand All @@ -181,4 +193,14 @@ WebClient.Builder webClientBuilder() {

}

@Configuration
protected static class RestTemplateTestConfig {

@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ void shouldCheckInstanceWithProvidedHealthCheckPathWithRestTemplate() {
assertThat(alive).isTrue();
}


@SuppressWarnings("ConstantConditions")
@Test
void shouldCheckInstanceWithDefaultHealthCheckPath() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,18 @@ private ServiceInstanceListSuppliersTestUtils() {

static BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunction(WebClient webClient) {
return (serviceInstance, healthCheckPath) -> webClient.get()
.uri(UriComponentsBuilder.fromUri(serviceInstance.getUri())
.path(healthCheckPath).build().toUri())
.uri(UriComponentsBuilder.fromUri(serviceInstance.getUri()).path(healthCheckPath).build().toUri())
.exchange().flatMap(clientResponse -> clientResponse.releaseBody()
.thenReturn(HttpStatus.OK.value() == clientResponse
.rawStatusCode()));
.thenReturn(HttpStatus.OK.value() == clientResponse.rawStatusCode()));
}

static BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunction(RestTemplate restTemplate) {
return (serviceInstance, healthCheckPath) -> Mono.defer(() -> {
URI uri = UriComponentsBuilder.fromUri(serviceInstance.getUri())
.path(healthCheckPath).build()
.toUri();
URI uri = UriComponentsBuilder.fromUri(serviceInstance.getUri()).path(healthCheckPath).build().toUri();
try {
return Mono.just(HttpStatus.OK
.equals(restTemplate.getForEntity(uri, Void.class)
.getStatusCode()));
} catch (Exception ignored){
return Mono.just(HttpStatus.OK.equals(restTemplate.getForEntity(uri, Void.class).getStatusCode()));
}
catch (Exception ignored) {
return Mono.just(false);
}
});
Expand Down