@LoadBalanced RestClient (#1294)

This commit is contained in:
Olga Maciaszek-Sharma
2023-11-13 18:12:19 +01:00
committed by GitHub
parent 42ba0eef31
commit dd47ca6c29
17 changed files with 634 additions and 235 deletions

View File

@@ -48,6 +48,7 @@ import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
@@ -188,6 +189,16 @@ public class LoadBalancerClientConfiguration {
.build(context);
}
@Bean
@ConditionalOnBean({ DiscoveryClient.class, RestClient.class })
@ConditionalOnMissingBean
@Conditional(HealthCheckConfigurationCondition.class)
public ServiceInstanceListSupplier healthCheckRestClientDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
.withBlockingRestClientHealthChecks().build(context);
}
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.BlockingRestClassesPresentCondition;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
@@ -35,9 +36,9 @@ import org.springframework.cloud.loadbalancer.blocking.retry.BlockingLoadBalance
import org.springframework.cloud.loadbalancer.core.LoadBalancerServiceInstanceCookieTransformer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
/**
* An autoconfiguration for {@link BlockingLoadBalancerClient}.
@@ -50,7 +51,7 @@ import org.springframework.web.client.RestTemplate;
@LoadBalancerClients
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
@AutoConfigureBefore({ org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.class })
@ConditionalOnClass(RestTemplate.class)
@Conditional(BlockingRestClassesPresentCondition.class)
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.enabled", havingValue = "true", matchIfMissing = true)
public class BlockingLoadBalancerClientAutoConfiguration {

View File

@@ -39,6 +39,7 @@ import org.springframework.core.env.PropertyResolver;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
@@ -203,6 +204,21 @@ public final class ServiceInstanceListSupplierBuilder {
return this;
}
/**
* Adds a {@link HealthCheckServiceInstanceListSupplier} that uses user-provided
* {@link RestClient} instance to the {@link ServiceInstanceListSupplier} hierarchy.
* @return the {@link ServiceInstanceListSupplierBuilder} object
*/
public ServiceInstanceListSupplierBuilder withBlockingRestClientHealthChecks() {
DelegateCreator creator = (context, delegate) -> {
RestClient restClient = context.getBean(RestClient.class);
LoadBalancerClientFactory loadBalancerClientFactory = context.getBean(LoadBalancerClientFactory.class);
return blockingHealthCheckServiceInstanceListSupplier(restClient, delegate, loadBalancerClientFactory);
};
this.creators.add(creator);
return this;
}
/**
* Adds a {@link HealthCheckServiceInstanceListSupplier} that uses user-provided
* {@link RestTemplate} instance to the {@link ServiceInstanceListSupplier} hierarchy.
@@ -218,6 +234,21 @@ public final class ServiceInstanceListSupplierBuilder {
return this;
}
/**
* Adds a {@link HealthCheckServiceInstanceListSupplier} that uses user-provided
* {@link RestClient} instance to the {@link ServiceInstanceListSupplier} hierarchy.
* @param restClient a user-provided {@link RestClient} instance
* @return the {@link ServiceInstanceListSupplierBuilder} object
*/
public ServiceInstanceListSupplierBuilder withBlockingHealthChecks(RestClient restClient) {
DelegateCreator creator = (context, delegate) -> {
LoadBalancerClientFactory loadBalancerClientFactory = context.getBean(LoadBalancerClientFactory.class);
return blockingHealthCheckServiceInstanceListSupplier(restClient, delegate, loadBalancerClientFactory);
};
this.creators.add(creator);
return this;
}
/**
* Adds a {@link ZonePreferenceServiceInstanceListSupplier} to the
* {@link ServiceInstanceListSupplier} hierarchy.
@@ -371,6 +402,22 @@ public final class ServiceInstanceListSupplierBuilder {
}));
}
private ServiceInstanceListSupplier blockingHealthCheckServiceInstanceListSupplier(RestClient restClient,
ServiceInstanceListSupplier delegate, LoadBalancerClientFactory loadBalancerClientFactory) {
return new HealthCheckServiceInstanceListSupplier(delegate, loadBalancerClientFactory,
(serviceInstance, healthCheckPath) -> Mono.defer(() -> {
URI uri = UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build()
.toUri();
try {
return Mono.just(HttpStatus.OK
.equals(restClient.get().uri(uri).retrieve().toBodilessEntity().getStatusCode()));
}
catch (Exception ignored) {
return Mono.just(false);
}
}));
}
static String getUri(ServiceInstance serviceInstance, String healthCheckPath) {
if (StringUtils.hasText(healthCheckPath)) {
String path = healthCheckPath.startsWith("/") ? healthCheckPath : "/" + healthCheckPath;

View File

@@ -16,7 +16,12 @@
package org.springframework.cloud.loadbalancer.annotation;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
@@ -38,6 +43,7 @@ import org.springframework.cloud.loadbalancer.core.ZonePreferenceServiceInstance
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
@@ -191,9 +197,10 @@ class LoadBalancerClientConfigurationTests {
}
@Test
void shouldInstantiateBlockingHealthCheckServiceInstanceListSupplier() {
blockingDiscoveryClientRunner.withUserConfiguration(RestTemplateTestConfig.class)
@ParameterizedTest
@MethodSource("blockingConfigurations")
void shouldInstantiateBlockingHealthCheckServiceInstanceListSupplier(Class<?> configurationClass) {
blockingDiscoveryClientRunner.withUserConfiguration(configurationClass)
.withPropertyValues("spring.cloud.loadbalancer.configurations=health-check").run(context -> {
ServiceInstanceListSupplier supplier = context.getBean(ServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(HealthCheckServiceInstanceListSupplier.class);
@@ -204,8 +211,8 @@ class LoadBalancerClientConfigurationTests {
@Test
void shouldInstantiateBlockingWeightedServiceInstanceListSupplier() {
blockingDiscoveryClientRunner.withUserConfiguration(RestTemplateTestConfig.class)
.withPropertyValues("spring.cloud.loadbalancer.configurations=weighted").run(context -> {
blockingDiscoveryClientRunner.withPropertyValues("spring.cloud.loadbalancer.configurations=weighted")
.run(context -> {
ServiceInstanceListSupplier supplier = context.getBean(ServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(WeightedServiceInstanceListSupplier.class);
ServiceInstanceListSupplier delegate = ((DelegatingServiceInstanceListSupplier) supplier)
@@ -216,6 +223,11 @@ class LoadBalancerClientConfigurationTests {
});
}
private static Stream<Arguments> blockingConfigurations() {
return Stream.of(Arguments.of(RestTemplateTestConfig.class), Arguments.of(RestClientTestConfig.class),
Arguments.of(RestTemplateAndRestClientConfig.class));
}
@Configuration
protected static class TestConfig {
@@ -237,4 +249,29 @@ class LoadBalancerClientConfigurationTests {
}
@Configuration
protected static class RestClientTestConfig {
@Bean
RestClient restClient() {
return RestClient.create();
}
}
@Configuration
protected static class RestTemplateAndRestClientConfig {
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
RestClient restClient() {
return RestClient.create();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +49,7 @@ class BlockingLoadBalancerClientAutoConfigurationTests {
@Test
public void worksWithoutSpringWeb() {
applicationContextRunner.withClassLoader(new FilteredClassLoader(RestTemplate.class))
applicationContextRunner.withClassLoader(new FilteredClassLoader(RestTemplate.class, RestClient.class))
.run(context -> assertThat(context).doesNotHaveBean(BlockingLoadBalancerClient.class));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import org.assertj.core.api.Assertions;
import org.assertj.core.util.Lists;
@@ -29,6 +30,9 @@ import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -47,6 +51,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
@@ -79,10 +84,6 @@ class HealthCheckServiceInstanceListSupplierTests {
@LocalServerPort
private int port;
private final WebClient webClient = WebClient.create();
private final RestTemplate restTemplate = new RestTemplate();
private LoadBalancerProperties properties;
private HealthCheckServiceInstanceListSupplier listSupplier;
@@ -109,7 +110,7 @@ class HealthCheckServiceInstanceListSupplierTests {
false);
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance),
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction(webClient));
buildLoadBalancerClientFactory(serviceId, properties), webClientHealthCheckFunction());
boolean alive = listSupplier.isAlive(serviceInstance).block();
@@ -151,15 +152,17 @@ class HealthCheckServiceInstanceListSupplierTests {
}
@SuppressWarnings("ConstantConditions")
@Test
void shouldCheckInstanceWithProvidedHealthCheckPathWithRestTemplate() {
@ParameterizedTest
@MethodSource("healthCheckFunctions")
void shouldCheckInstanceWithProvidedHealthCheckPath(
BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunction) {
String serviceId = "ignored-service";
properties.getHealthCheck().getPath().put("ignored-service", "/health");
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1", serviceId, "127.0.0.1", port,
false);
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance),
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction(restTemplate));
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction);
boolean alive = listSupplier.isAlive(serviceInstance).block();
@@ -167,46 +170,16 @@ class HealthCheckServiceInstanceListSupplierTests {
}
@SuppressWarnings("ConstantConditions")
@Test
void shouldCheckInstanceWithDefaultHealthCheckPath() {
String serviceId = "ignored-service";
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1", serviceId, "127.0.0.1", port,
false);
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance),
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction(webClient));
boolean alive = listSupplier.isAlive(serviceInstance).block();
assertThat(alive).isTrue();
}
@SuppressWarnings("ConstantConditions")
@Test
void shouldReturnFalseIfEndpointNotFound() {
@ParameterizedTest
@MethodSource("healthCheckFunctions")
void shouldReturnFalseIfEndpointNotFound(BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunction) {
String serviceId = "ignored-service";
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1", serviceId, "127.0.0.1", port,
false);
properties.getHealthCheck().getPath().put(serviceId, "/test");
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance),
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction(webClient));
boolean alive = listSupplier.isAlive(serviceInstance).block();
assertThat(alive).isFalse();
}
@SuppressWarnings("ConstantConditions")
@Test
void shouldReturnFalseIfEndpointNotFoundWithRestTemplate() {
String serviceId = "ignored-service";
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1", serviceId, "127.0.0.1", port,
false);
properties.getHealthCheck().getPath().put(serviceId, "/test");
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance),
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction(restTemplate));
buildLoadBalancerClientFactory(serviceId, properties), healthCheckFunction);
boolean alive = listSupplier.isAlive(serviceInstance).block();
@@ -232,7 +205,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.doReturn(Mono.just(false)).when(mock).isAlive(serviceInstance2);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return mock.isAlive(serviceInstance);
@@ -263,7 +236,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.doReturn(Mono.just(true)).when(mock).isAlive(serviceInstance2);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return mock.isAlive(serviceInstance);
@@ -296,7 +269,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.doReturn(Mono.just(true)).when(mock).isAlive(serviceInstance2);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return mock.isAlive(serviceInstance);
@@ -327,7 +300,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.doReturn(Mono.error(new RuntimeException("boom"))).when(mock).isAlive(serviceInstance2);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return mock.isAlive(serviceInstance);
@@ -353,7 +326,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.when(delegate.getServiceId()).thenReturn(SERVICE_ID);
Mockito.when(delegate.get()).thenReturn(Flux.just(Lists.list(serviceInstance1, serviceInstance2)));
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
if (serviceInstance == serviceInstance1) {
@@ -381,7 +354,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.when(delegate.getServiceId()).thenReturn(SERVICE_ID);
Mockito.when(delegate.get()).thenReturn(Flux.just(Lists.list(serviceInstance1)));
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return Mono.just(true);
@@ -414,7 +387,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.doReturn(Mono.error(new RuntimeException("boom"))).when(mock).isAlive(serviceInstance2);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return mock.isAlive(serviceInstance);
@@ -443,7 +416,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.when(mock.isAlive(serviceInstance1)).thenReturn(Mono.never(), Mono.just(true));
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return mock.isAlive(serviceInstance);
@@ -475,7 +448,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.when(delegate.get()).thenReturn(instances);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return Mono.just(true);
@@ -510,7 +483,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.when(delegate.get()).thenReturn(instances);
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return Mono.just(true);
@@ -543,7 +516,7 @@ class HealthCheckServiceInstanceListSupplierTests {
when(delegate.get()).thenReturn(Flux.just(Collections.singletonList(serviceInstance1)))
.thenReturn(Flux.just(Collections.singletonList(serviceInstance2)));
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return Mono.just(true);
@@ -572,7 +545,7 @@ class HealthCheckServiceInstanceListSupplierTests {
when(delegate.getServiceId()).thenReturn(SERVICE_ID);
when(delegate.get()).thenReturn(Flux.just(Collections.singletonList(serviceInstance1)))
.thenReturn(Flux.just(Collections.singletonList(serviceInstance2)));
BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunc = healthCheckFunction(webClient);
BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunc = webClientHealthCheckFunction();
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunc) {
@Override
@@ -601,7 +574,7 @@ class HealthCheckServiceInstanceListSupplierTests {
Mockito.when(delegate.get()).thenReturn(Flux.just(Lists.list(serviceInstance1)));
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient)) {
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction()) {
@Override
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
return Mono.just(true);
@@ -633,7 +606,7 @@ class HealthCheckServiceInstanceListSupplierTests {
.doOnSubscribe(subscription -> subscribed.set(true)).doOnCancel(instancesCanceled::incrementAndGet));
listSupplier = new HealthCheckServiceInstanceListSupplier(delegate,
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient));
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction());
listSupplier.afterPropertiesSet();
@@ -656,7 +629,7 @@ class HealthCheckServiceInstanceListSupplierTests {
false);
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance),
buildLoadBalancerClientFactory(SERVICE_ID, properties), healthCheckFunction(webClient));
buildLoadBalancerClientFactory(SERVICE_ID, properties), webClientHealthCheckFunction());
boolean alive = listSupplier.isAlive(serviceInstance).block();
@@ -674,7 +647,7 @@ class HealthCheckServiceInstanceListSupplierTests {
port, false);
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance), loadBalancerClientFactory,
healthCheckFunction(webClient));
webClientHealthCheckFunction());
listSupplier.isAlive(serviceInstance).block();
});
@@ -682,6 +655,18 @@ class HealthCheckServiceInstanceListSupplierTests {
assertThat(exception).hasMessageContaining("Connection refused: /127.0.0.1:888");
}
private static Stream<Arguments> healthCheckFunctions() {
RestTemplate restTemplate = new RestTemplate();
RestClient restClient = RestClient.create();
return Stream.of(Arguments.of(healthCheckFunction(restTemplate)), Arguments.of(healthCheckFunction(restClient)),
Arguments.of(webClientHealthCheckFunction()));
}
private static BiFunction<ServiceInstance, String, Mono<Boolean>> webClientHealthCheckFunction() {
WebClient webClient = WebClient.create();
return healthCheckFunction(webClient);
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
@@ -61,4 +62,17 @@ final class ServiceInstanceListSuppliersTestUtils {
});
}
static BiFunction<ServiceInstance, String, Mono<Boolean>> healthCheckFunction(RestClient restClient) {
return (serviceInstance, healthCheckPath) -> Mono.defer(() -> {
URI uri = UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build().toUri();
try {
return Mono.just(
HttpStatus.OK.equals(restClient.get().uri(uri).retrieve().toBodilessEntity().getStatusCode()));
}
catch (Exception ignored) {
return Mono.just(false);
}
});
}
}