Browse Source

Support setting custom port for LB health-checks. (#1039)

pull/1045/head
Olga Maciaszek-Sharma 3 years ago committed by GitHub
parent
commit
d639552fb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docs/src/main/asciidoc/spring-cloud-commons.adoc
  2. 19
      spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerProperties.java
  3. 13
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplier.java
  4. 23
      spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java

2
docs/src/main/asciidoc/spring-cloud-commons.adoc

@ -979,7 +979,7 @@ TIP: The `HealthCheckServiceInstanceListSupplier` relies on having updated insta
`HealthCheckServiceInstanceListSupplier` uses properties prefixed with `HealthCheckServiceInstanceListSupplier` uses properties prefixed with
`spring.cloud.loadbalancer.health-check`. You can set the `initialDelay` and `interval` `spring.cloud.loadbalancer.health-check`. You can set the `initialDelay` and `interval`
for the scheduler. You can set the default path for the healthcheck URL by setting for the scheduler. You can set the default path for the healthcheck URL by setting
the value of the `spring.cloud.loadbalancer.health-check.path.default` property. You can also set a specific value for any given service by setting the value of the `spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]` property, substituting `[SERVICE_ID]` with the correct ID of your service. If the `[SERVICE_ID]` is not specified, `/actuator/health` is used by default. If the `[SERVICE_ID]` is set to `null` or empty as a value, then the health check will not be executed. the value of the `spring.cloud.loadbalancer.health-check.path.default` property. You can also set a specific value for any given service by setting the value of the `spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]` property, substituting `[SERVICE_ID]` with the correct ID of your service. If the `[SERVICE_ID]` is not specified, `/actuator/health` is used by default. If the `[SERVICE_ID]` is set to `null` or empty as a value, then the health check will not be executed. You can also set a custom port for health-check requests by setting the value of `spring.cloud.loadbalancer.health-check.port`. If none is set, the port under which the requested service is available at the service instance.
TIP: If you rely on the default path (`/actuator/health`), make sure you add `spring-boot-starter-actuator` to your collaborator's dependencies, unless you are planning to add such an endpoint on your own. TIP: If you rely on the default path (`/actuator/health`), make sure you add `spring-boot-starter-actuator` to your collaborator's dependencies, unless you are planning to add such an endpoint on your own.

19
spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerProperties.java

@ -186,8 +186,19 @@ public class LoadBalancerProperties {
*/ */
private Duration refetchInstancesInterval = Duration.ofSeconds(25); private Duration refetchInstancesInterval = Duration.ofSeconds(25);
/**
* Path at which the health-check request should be made. Can be set up per
* <code>serviceId</code>. A <code>default</code> value can be set up as well. If
* none is set up, <code>/actuator/health</code> will be used.
*/
private Map<String, String> path = new LinkedCaseInsensitiveMap<>(); private Map<String, String> path = new LinkedCaseInsensitiveMap<>();
/**
* Path at which the health-check request should be made. If none is set, the port
* under which the requested service is available at the service instance.
*/
private Integer port;
/** /**
* Indicates whether the instances should be refetched by the * Indicates whether the instances should be refetched by the
* <code>HealthCheckServiceInstanceListSupplier</code>. This can be used if the * <code>HealthCheckServiceInstanceListSupplier</code>. This can be used if the
@ -251,6 +262,14 @@ public class LoadBalancerProperties {
this.interval = interval; this.interval = interval;
} }
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
} }
public static class Retry { public static class Retry {

13
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplier.java

@ -30,6 +30,7 @@ import reactor.retry.Repeat;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
@ -156,7 +157,7 @@ public class HealthCheckServiceInstanceListSupplier extends DelegatingServiceIns
return Mono.just(true); return Mono.just(true);
} }
String healthCheckPath = healthCheckPropertyValue != null ? healthCheckPropertyValue : defaultHealthCheckPath; String healthCheckPath = healthCheckPropertyValue != null ? healthCheckPropertyValue : defaultHealthCheckPath;
return aliveFunction.apply(serviceInstance, healthCheckPath); return aliveFunction.apply(updatedServiceInstance(serviceInstance), healthCheckPath);
} }
@Override @Override
@ -167,4 +168,14 @@ public class HealthCheckServiceInstanceListSupplier extends DelegatingServiceIns
} }
} }
private ServiceInstance updatedServiceInstance(ServiceInstance serviceInstance) {
Integer healthCheckPort = healthCheck.getPort();
if (serviceInstance instanceof DefaultServiceInstance && healthCheckPort != null) {
return new DefaultServiceInstance(serviceInstance.getInstanceId(), serviceInstance.getServiceId(),
serviceInstance.getHost(), healthCheckPort, serviceInstance.isSecure(),
serviceInstance.getMetadata());
}
return serviceInstance;
}
} }

23
spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java

@ -41,6 +41,7 @@ import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers; import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -50,6 +51,7 @@ import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@ -579,6 +581,27 @@ class HealthCheckServiceInstanceListSupplierTests {
assertThat(alive).isTrue(); assertThat(alive).isTrue();
} }
@Test
void shouldCheckUseProvidedPortForHealthCheckRequest() {
Throwable exception = catchThrowable(() -> {
String serviceId = "ignored-service";
healthCheck.setPort(8888);
LoadBalancerProperties properties = new LoadBalancerProperties();
properties.setHealthCheck(healthCheck);
LoadBalancerClientFactory loadBalancerClientFactory = mock(LoadBalancerClientFactory.class);
when(loadBalancerClientFactory.getProperties(serviceId)).thenReturn(properties);
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1", serviceId, "127.0.0.1",
port, false);
listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSuppliers.from(serviceId, serviceInstance), loadBalancerClientFactory,
healthCheckFunction(webClient));
listSupplier.isAlive(serviceInstance).block();
});
assertThat(exception).hasMessageContaining("Connection refused: /127.0.0.1:888");
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration @EnableAutoConfiguration
@RestController @RestController

Loading…
Cancel
Save