diff --git a/docs/modules/ROOT/pages/spring-cloud-commons/loadbalancer.adoc b/docs/modules/ROOT/pages/spring-cloud-commons/loadbalancer.adoc index d98704d7..56926206 100644 --- a/docs/modules/ROOT/pages/spring-cloud-commons/loadbalancer.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-commons/loadbalancer.adoc @@ -204,7 +204,7 @@ WARNING: If using any of the Service Discovery-backed suppliers, adding this hea from the Service Registry. TIP: The `HealthCheckServiceInstanceListSupplier` relies on having updated instances provided by a delegate flux. In the rare cases when you want to use a delegate that does not refresh the instances, even though the list of instances may change (such as the `DiscoveryClientServiceInstanceListSupplier` provided by us), you can set `spring.cloud.loadbalancer.health-check.refetch-instances` to `true` to have the instance list refreshed by the `HealthCheckServiceInstanceListSupplier`. You can then also adjust the refretch intervals by modifying the value of `spring.cloud.loadbalancer.health-check.refetch-instances-interval` and opt to disable the additional healthcheck repetitions by setting `spring.cloud.loadbalancer.health-check.repeat-health-check` to `false` as every instances refetch - will also trigger a healthcheck. +will also trigger a healthcheck. `HealthCheckServiceInstanceListSupplier` uses properties prefixed with `spring.cloud.loadbalancer.health-check`. You can set the `initialDelay` and `interval` @@ -561,7 +561,7 @@ Since `5.0.0`, Spring Cloud LoadBalancer supports https://docs.spring.io/spring- For each Interface Client group, if the group `baseUrl` (defined under the `spring.http.client.service.group.[groupName].base-url` property) is `null`, a `serviceId`-based URL for load-balancing is set up as the `baseUrl`, with `serviceId` resolved from the Interface Client `groupName`. -If the group `baseUrl` is `null` or it is already a `serviceId`-based URL, a +If the group `baseUrl` is `null` or its scheme is set to `lb`, a `DeferringLoadBalancerInterceptor` instance is picked from the application context for blocking scenarios, and a `DeferringLoadBalancerExchangeFilterFunction` instance for reactive scenarios, and is added to the group's `RestClient.Builder` or `WebClient.Builder` if available, allowing for the requests to be load-balanced. For example, in an app with the following Interface Clients configuration: @@ -579,10 +579,11 @@ public class HttpVerificationClientApplication { } ---- -If the `spring.http.client.service.group.verificationClient.base-url` property is not set, it will be automatically set to `http://verificationClient`. The default scheme (`http`) is used initially; however, if a secure `ServiceInstance` is selected through load-balancing, it will be changed to `https`. +If the `spring.http.client.service.group.verificationClient.base-url` property is not set, it will be automatically set to `http://verificationClient`. +The default scheme (`http`) is used initially; however, if a secure `ServiceInstance` is selected through load-balancing, it will be changed to `https`. -If the `spring.http.client.service.group.verificationClient.base-url` property is set to a URL that uses the `groupName` / `serviceId` as the host (for example, `https://verificationClient/path`), it will be left unchanged. In both of these cases, either a `DeferringLoadBalancerInterceptor` or `DeferringLoadBalancerExchangeFilterFunction` will be added to the group's client builder, enabling the requests to be load-balanced. +If the `spring.http.client.service.group.verificationClient.base-url` property is set to a URL that has the `lb` scheme, (for example, `lb://verificationClient/path`), it will be used with `http` being initially set as the default scheme. +If a secure `ServiceInstance` is selected through load-balancing, the scheme will be changed to `https`. +In both of these cases, either a `DeferringLoadBalancerInterceptor` or `DeferringLoadBalancerExchangeFilterFunction` will be added to the group's client builder, enabling the requests to be load-balanced. -If the `spring.http.client.service.group.verificationClient.base-url` property is set to a URL that does not have `verificationClient` as the host (for example, `http://someOtherHost/path`), no load-balancer integration will be applied. - -TIP: To use a different default scheme instead of `http` (for example, `ws`, which would be converted to `wss` if a secure `ServiceInstance` is selected), set the `spring.http.client.service.group.verificationClient.base-url` property with the desired scheme. +If the `spring.http.client.service.group.verificationClient.base-url` property is set to a URL that does not have the scheme set to `lb`, (for example, `lb://verificationClient/path`), no load-balancer integration will be applied. diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurer.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurer.java index 672c16bd..350bcca9 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurer.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurer.java @@ -25,9 +25,10 @@ import org.springframework.boot.autoconfigure.http.client.service.HttpClientServ import org.springframework.util.function.SingletonSupplier; import org.springframework.web.client.RestClient; import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer; +import org.springframework.web.util.UriComponentsBuilder; +import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.DEFAULT_SCHEME; import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.constructInterfaceClientsBaseUrl; -import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.isServiceIdUrl; /** * Load-balancer-specific {@link RestClientHttpServiceGroupConfigurer} implementation. If @@ -71,12 +72,16 @@ public class LoadBalancerRestClientHttpServiceGroupConfigurer implements RestCli groups.configureClient((group, builder) -> { String groupName = group.name(); HttpClientServiceProperties.Group groupProperties = clientServiceProperties.getGroup().get(groupName); - if (groupProperties == null || groupProperties.getBaseUrl() == null) { + String baseUrlString = groupProperties == null ? null : groupProperties.getBaseUrl(); + URI existingBaseUrl = baseUrlString == null ? null : URI.create(baseUrlString); + if (existingBaseUrl == null) { URI baseUrl = constructBaseUrl(groupName); builder.baseUrl(baseUrl); builder.requestInterceptor(loadBalancerInterceptor); } - else if (isServiceIdUrl(groupProperties.getBaseUrl(), groupName)) { + else if ("lb".equalsIgnoreCase(existingBaseUrl.getScheme())) { + URI baseUrl = UriComponentsBuilder.fromUri(existingBaseUrl).scheme(DEFAULT_SCHEME).build().toUri(); + builder.baseUrl(baseUrl); builder.requestInterceptor(loadBalancerInterceptor); } }); diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java index ace12bd1..d25cff78 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java @@ -17,15 +17,11 @@ package org.springframework.cloud.client.loadbalancer; import java.net.URI; -import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.cloud.client.ServiceInstance; import org.springframework.web.util.UriComponentsBuilder; @@ -39,16 +35,17 @@ public final class LoadBalancerUriTools { throw new IllegalStateException("Can't instantiate a utility class"); } - private static final Log LOG = LogFactory.getLog(LoadBalancerUriTools.class); - private static final String PERCENTAGE_SIGN = "%"; - private static final String DEFAULT_SCHEME = "http"; - private static final String DEFAULT_SECURE_SCHEME = "https"; private static final Map INSECURE_SCHEME_MAPPINGS; + /** + * Default scheme. + */ + public static final String DEFAULT_SCHEME = "http"; + static { INSECURE_SCHEME_MAPPINGS = new HashMap<>(); INSECURE_SCHEME_MAPPINGS.put(DEFAULT_SCHEME, DEFAULT_SECURE_SCHEME); @@ -125,24 +122,4 @@ public final class LoadBalancerUriTools { return UriComponentsBuilder.newInstance().scheme(DEFAULT_SCHEME).host(groupName).encode().build().toUri(); } - public static boolean isServiceIdUrl(String baseUrlString, String serviceId) { - if (serviceId == null) { - return false; - } - if (baseUrlString == null) { - return false; - } - URI baseUrl; - try { - baseUrl = new URI(baseUrlString); - } - catch (URISyntaxException e) { - if (LOG.isErrorEnabled()) { - LOG.error("Incorrect baseUrl String syntax", e); - } - return false; - } - return serviceId.equals(baseUrl.getHost()); - } - } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurer.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurer.java index cdd51ae2..0d42aef8 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurer.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurer.java @@ -25,9 +25,10 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools; import org.springframework.util.function.SingletonSupplier; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer; +import org.springframework.web.util.UriComponentsBuilder; +import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.DEFAULT_SCHEME; import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.constructInterfaceClientsBaseUrl; -import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.isServiceIdUrl; /** * Load-balancer-specific {@link WebClientHttpServiceGroupConfigurer} implementation. If @@ -73,12 +74,19 @@ public class LoadBalancerWebClientHttpServiceGroupConfigurer implements WebClien String groupName = group.name(); ReactiveHttpClientServiceProperties.Group groupProperties = clientServiceProperties.getGroup() .get(groupName); - if (groupProperties == null || groupProperties.getBaseUrl() == null) { + String baseUrlString = groupProperties == null ? null : groupProperties.getBaseUrl(); + URI existingBaseUrl = baseUrlString == null ? null : URI.create(baseUrlString); + if (existingBaseUrl == null) { URI baseUrl = constructBaseUrl(groupName); builder.baseUrl(String.valueOf(baseUrl)); builder.filter(loadBalancerFilterFunction); } - else if (isServiceIdUrl(groupProperties.getBaseUrl(), groupName)) { + else if ("lb".equalsIgnoreCase(existingBaseUrl.getScheme())) { + String baseUrl = UriComponentsBuilder.fromUri(existingBaseUrl) + .scheme(DEFAULT_SCHEME) + .build() + .toUriString(); + builder.baseUrl(baseUrl); builder.filter(loadBalancerFilterFunction); } }); diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurerTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurerTests.java index 8d6ba31a..35c0c3c9 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurerTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientHttpServiceGroupConfigurerTests.java @@ -72,9 +72,9 @@ class LoadBalancerRestClientHttpServiceGroupConfigurerTests { } @Test - void shouldAddInterceptorWhenBaseUrlIsServiceIdUrl() { + void shouldAddInterceptorWhenBaseUrlHasLbScheme() { Group group = new Group(); - group.setBaseUrl("https://" + GROUP_NAME + "/path"); + group.setBaseUrl("lb://" + GROUP_NAME + "/path"); clientServiceProperties.getGroup().put(GROUP_NAME, group); LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer( interceptorProvider, clientServiceProperties); @@ -89,9 +89,9 @@ class LoadBalancerRestClientHttpServiceGroupConfigurerTests { } @Test - void shouldNotAddInterceptorWhenBaseUrlIsNotServiceIdUrl() { + void shouldNotAddInterceptorWhenBaseDoesNotHaveLbScheme() { Group group = new Group(); - group.setBaseUrl("https://some-other-service/path"); + group.setBaseUrl("http://" + GROUP_NAME + "/path"); clientServiceProperties.getGroup().put(GROUP_NAME, group); LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer( interceptorProvider, clientServiceProperties); diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriToolsTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriToolsTests.java index c700cfcc..be40f95a 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriToolsTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriToolsTests.java @@ -19,19 +19,13 @@ package org.springframework.cloud.client.loadbalancer; import java.net.URI; import java.util.LinkedHashMap; import java.util.Map; -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.cloud.client.ServiceInstance; -import org.springframework.web.util.InvalidUrlException; import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link LoadBalancerUriTools}. @@ -189,29 +183,6 @@ class LoadBalancerUriToolsTests { assertThat(reconstructed.getPort()).isEqualTo(serviceInstance.getPort()); } - @ParameterizedTest(name = "{index} => url={0}, serviceId={1}, expected={2}") - @MethodSource("provideUrlAndServiceIdForIsServiceIdUrl") - void verifyServiceIdUrl(String url, String serviceId, boolean expected) { - assertThat(LoadBalancerUriTools.isServiceIdUrl(url, serviceId)).isEqualTo(expected); - } - - @Test - void verifyServiceIdIncorrectUrl() { - assertThatExceptionOfType(InvalidUrlException.class).isThrownBy(() -> { - URI baseUrl = UriComponentsBuilder.fromUriString("https://:testService/xxx").build().toUri(); - - LoadBalancerUriTools.isServiceIdUrl(String.valueOf(baseUrl), null); - }); - - } - - private static Stream provideUrlAndServiceIdForIsServiceIdUrl() { - return Stream.of(org.junit.jupiter.params.provider.Arguments.of("https://testService/xxx", "testService", true), - org.junit.jupiter.params.provider.Arguments.of("https://test/xxx", "testService", false), - org.junit.jupiter.params.provider.Arguments.of("https://testService/xxx", null, false), - org.junit.jupiter.params.provider.Arguments.of(null, "testService", false)); - } - } class TestServiceInstance implements ServiceInstance { diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurerTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurerTests.java index 5916a7bd..fdff5a92 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurerTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientHttpServiceGroupConfigurerTests.java @@ -73,9 +73,9 @@ class LoadBalancerWebClientHttpServiceGroupConfigurerTests { } @Test - void shouldAddInterceptorWhenBaseUrlIsServiceIdUrl() { + void shouldAddInterceptorWhenBaseUrlHasLbScheme() { ReactiveHttpClientServiceProperties.Group group = new ReactiveHttpClientServiceProperties.Group(); - group.setBaseUrl("https://" + GROUP_NAME + "/path"); + group.setBaseUrl("lb://" + GROUP_NAME + "/path"); clientServiceProperties.getGroup().put(GROUP_NAME, group); LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer( exchangeFilterFunctionProvider, clientServiceProperties); @@ -90,9 +90,9 @@ class LoadBalancerWebClientHttpServiceGroupConfigurerTests { } @Test - void shouldNotAddInterceptorWhenBaseUrlIsNotServiceIdUrl() { + void shouldNotAddInterceptorWhenBaseDoesNotHaveLbScheme() { ReactiveHttpClientServiceProperties.Group group = new ReactiveHttpClientServiceProperties.Group(); - group.setBaseUrl("https://some-other-service/path"); + group.setBaseUrl("https://" + GROUP_NAME + "/path"); clientServiceProperties.getGroup().put(GROUP_NAME, group); LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer( exchangeFilterFunctionProvider, clientServiceProperties);