Add LoadBalancerWebClientHttpServiceGroupConfigurer. Refactor. Add javadocs.
Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
@@ -27,17 +26,33 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.http.client.service.HttpClientServiceProperties;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
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.constructInterfaceClientsBaseUrl;
|
||||
import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.isServiceIdUrl;
|
||||
|
||||
/**
|
||||
* Load-balancer-specific {@link RestClientHttpServiceGroupConfigurer} implementation. If
|
||||
* the group {@code baseUrl} is {@code null}, sets up a {@code baseUrl} with LoadBalancer
|
||||
* {@code serviceId} -resolved from Interface Client {@code groupName} set as
|
||||
* {@code host}. If the group {@code baseUrl} is {@code null} or
|
||||
* {@link LoadBalancerUriTools#isServiceIdUrl(String, String)}, a
|
||||
* {@link DeferringLoadBalancerInterceptor} instance picked from application context is
|
||||
* added to the group's {@link RestClient.Builder} if available, allowing for the requests
|
||||
* to be load-balanced.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 5.0.0
|
||||
* @see RestClientHttpServiceGroupConfigurer
|
||||
* @see HttpClientServiceProperties
|
||||
*/
|
||||
public class LoadBalancerRestClientHttpServiceGroupConfigurer implements RestClientHttpServiceGroupConfigurer {
|
||||
|
||||
// Make sure Boot's customisers run before
|
||||
private static final int ORDER = 10;
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(LoadBalancerRestClientHttpServiceGroupConfigurer.class);
|
||||
|
||||
private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;
|
||||
@@ -60,7 +75,8 @@ public class LoadBalancerRestClientHttpServiceGroupConfigurer implements RestCli
|
||||
public void configureGroups(@NonNull Groups<RestClient.Builder> groups) {
|
||||
DeferringLoadBalancerInterceptor loadBalancerInterceptor = loadBalancerInterceptorSupplier.get();
|
||||
if (loadBalancerInterceptor == null) {
|
||||
throw new IllegalStateException(ClientHttpRequestInterceptor.class.getSimpleName() + " not available.");
|
||||
throw new IllegalStateException(
|
||||
DeferringLoadBalancerInterceptor.class.getSimpleName() + " instance not available.");
|
||||
}
|
||||
groups.configureClient((group, builder) -> {
|
||||
String groupName = group.name();
|
||||
@@ -77,31 +93,15 @@ public class LoadBalancerRestClientHttpServiceGroupConfigurer implements RestCli
|
||||
|
||||
}
|
||||
|
||||
private boolean isServiceIdUrl(String baseUrlString, String groupName) {
|
||||
if (baseUrlString == null) {
|
||||
return false;
|
||||
}
|
||||
URI baseUrl;
|
||||
try {
|
||||
baseUrl = new URI(baseUrlString);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
if (LOG.isErrorEnabled()) {
|
||||
LOG.error("Incorrect baseUrlString syntax", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return groupName.equals(baseUrl.getHost());
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
private URI constructBaseUrl(String groupName) {
|
||||
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(groupName);
|
||||
return UriComponentsBuilder.newInstance()
|
||||
.scheme(loadBalancerProperties.getInterfaceClients().getDefaultScheme())
|
||||
.host(groupName)
|
||||
.encode()
|
||||
.build()
|
||||
.toUri();
|
||||
return constructInterfaceClientsBaseUrl(groupName,
|
||||
loadBalancerProperties.getInterfaceClients().getDefaultScheme());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
@@ -17,11 +17,15 @@
|
||||
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;
|
||||
|
||||
@@ -35,6 +39,8 @@ 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";
|
||||
@@ -115,4 +121,25 @@ public final class LoadBalancerUriTools {
|
||||
return originalOrDefault;
|
||||
}
|
||||
|
||||
public static URI constructInterfaceClientsBaseUrl(String groupName, String defaultScheme) {
|
||||
return UriComponentsBuilder.newInstance().scheme(defaultScheme).host(groupName).encode().build().toUri();
|
||||
}
|
||||
|
||||
public static boolean isServiceIdUrl(String baseUrlString, String serviceId) {
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
@@ -21,6 +21,9 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.http.client.service.HttpClientServiceProperties;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -66,6 +69,17 @@ public class LoadBalancerBeanPostProcessorAutoConfiguration {
|
||||
return new DeferringLoadBalancerExchangeFilterFunction<>(exchangeFilterFunctionProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({ HttpClientServiceProperties.class, ReactiveLoadBalancer.Factory.class })
|
||||
@ConditionalOnMissingBean(LoadBalancerWebClientHttpServiceGroupConfigurer.class)
|
||||
LoadBalancerWebClientHttpServiceGroupConfigurer loadBalancerRestClientHttpServiceGroupConfigurer(
|
||||
ObjectProvider<DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction>> deferringExchangeFilterFunction,
|
||||
HttpClientServiceProperties properties,
|
||||
ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
|
||||
return new LoadBalancerWebClientHttpServiceGroupConfigurer(deferringExchangeFilterFunction, properties,
|
||||
loadBalancerFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class OnAnyLoadBalancerImplementationPresentCondition extends AnyNestedCondition {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.client.loadbalancer.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.http.client.service.HttpClientServiceProperties;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
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 static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.constructInterfaceClientsBaseUrl;
|
||||
import static org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools.isServiceIdUrl;
|
||||
|
||||
/**
|
||||
* Load-balancer-specific {@link WebClientHttpServiceGroupConfigurer} implementation. If
|
||||
* the group {@code baseUrl} is {@code null}, sets up a {@code baseUrl} with LoadBalancer
|
||||
* {@code serviceId} -resolved from Interface Client {@code groupName} set as
|
||||
* {@code host}. If the group {@code baseUrl} is {@code null} or
|
||||
* {@link LoadBalancerUriTools#isServiceIdUrl(String, String)}, a
|
||||
* {@link DeferringLoadBalancerExchangeFilterFunction} instance picked from application
|
||||
* context is added to the group's {@link WebClient.Builder} if available, allowing for
|
||||
* the requests to be load-balanced.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 5.0.0
|
||||
* @see WebClient.Builder
|
||||
* @see HttpClientServiceProperties
|
||||
*/
|
||||
public class LoadBalancerWebClientHttpServiceGroupConfigurer implements WebClientHttpServiceGroupConfigurer {
|
||||
|
||||
// Make sure Boot's customisers run before
|
||||
private static final int ORDER = 10;
|
||||
|
||||
private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;
|
||||
|
||||
private final SingletonSupplier<DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction>> loadBalancerFilterFunctionSupplier;
|
||||
|
||||
private final HttpClientServiceProperties clientServiceProperties;
|
||||
|
||||
public LoadBalancerWebClientHttpServiceGroupConfigurer(
|
||||
ObjectProvider<DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction>> exchangeFilterFunctionProvider,
|
||||
HttpClientServiceProperties clientServiceProperties,
|
||||
ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory) {
|
||||
this.loadBalancerFilterFunctionSupplier = SingletonSupplier
|
||||
.ofNullable(exchangeFilterFunctionProvider.getIfAvailable());
|
||||
this.clientServiceProperties = clientServiceProperties;
|
||||
this.loadBalancerClientFactory = loadBalancerClientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureGroups(Groups<WebClient.Builder> groups) {
|
||||
DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction> loadBalancerFilterFunction = loadBalancerFilterFunctionSupplier
|
||||
.get();
|
||||
if (loadBalancerFilterFunction == null) {
|
||||
throw new IllegalStateException(
|
||||
DeferringLoadBalancerExchangeFilterFunction.class.getSimpleName() + " instance not available.");
|
||||
}
|
||||
groups.configureClient((group, builder) -> {
|
||||
String groupName = group.name();
|
||||
HttpClientServiceProperties.Group groupProperties = clientServiceProperties.getGroup().get(groupName);
|
||||
if (groupProperties == null || groupProperties.getBaseUrl() == null) {
|
||||
URI baseUrl = constructBaseUrl(groupName);
|
||||
builder.baseUrl(String.valueOf(baseUrl));
|
||||
builder.filter(loadBalancerFilterFunction);
|
||||
}
|
||||
else if (isServiceIdUrl(groupProperties.getBaseUrl(), groupName)) {
|
||||
builder.filter(loadBalancerFilterFunction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
private URI constructBaseUrl(String groupName) {
|
||||
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(groupName);
|
||||
return constructInterfaceClientsBaseUrl(groupName,
|
||||
loadBalancerProperties.getInterfaceClients().getDefaultScheme());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user