Add docs. Reformat.

Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>
This commit is contained in:
Olga Maciaszek-Sharma
2025-05-14 14:23:30 +02:00
parent 1ef0b0a96a
commit 00ac6b3b41
3 changed files with 64 additions and 62 deletions

View File

@@ -554,3 +554,14 @@ NOTE: Starting with `4.1.0`, we have introduced the `callGetWithRequestOnDelegat
Since `4.0.0`, Spring Cloud LoadBalancer supports Spring AOT transformations and native images. However, to use this feature, you need to explicitly define your `LoadBalancerClient` service IDs. You can do so by using the `value` or `name` attributes of the `@LoadBalancerClient` annotation or as values of the `spring.cloud.loadbalancer.eager-load.clients` property.
== LoadBalancer Integration for Spring Interface Clients AutoConfiguration
Since `5.0.0`, Spring Cloud LoadBalancer supports https://docs.spring.io/spring-framework/reference/7.0-SNAPSHOT/integration/rest-clients.html#rest-http-interface[Spring Interface Clients] AutoConfiguration through the `LoadBalancerRestClientHttpServiceGroupConfigurer` and `LoadBalancerWebClientHttpServiceGroupConfigurer`.
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`. The default scheme for the load-balanced `baseUrl` (`http`, by default) can be set through the `spring.cloud.loadbalancer.interface-clients.default-scheme` property.
If the group `baseUrl` is `null` or it is already a `serviceId`-based URL, 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.

View File

@@ -44,12 +44,15 @@ import static org.mockito.Mockito.when;
*
* @author Olga Maciaszek-Sharma
*/
@SuppressWarnings({"unchecked", "removal"})
@SuppressWarnings({ "unchecked", "removal" })
class LoadBalancerRestClientHttpServiceGroupConfigurerTests {
private static final String GROUP_NAME = "testService";
private ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;
private HttpClientServiceProperties clientServiceProperties;
private ObjectProvider<DeferringLoadBalancerInterceptor> interceptorProvider;
@BeforeEach
@@ -64,39 +67,33 @@ class LoadBalancerRestClientHttpServiceGroupConfigurerTests {
@Test
void shouldAddInterceptorWhenBaseUrlIsNotSet() {
LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer(interceptorProvider,
clientServiceProperties, loadBalancerClientFactory);
LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer(
interceptorProvider, clientServiceProperties, loadBalancerClientFactory);
TestGroups groups = new TestGroups();
configurer.configureGroups(groups);
groups.builder.requestInterceptors(interceptors -> {
assertThat(interceptors).hasSize(1);
assertThat(interceptors.get(0)
.getClass()).isEqualTo(DeferringLoadBalancerInterceptor.class);
}
);
assertThat(interceptors).hasSize(1);
assertThat(interceptors.get(0).getClass()).isEqualTo(DeferringLoadBalancerInterceptor.class);
});
}
// properties.getInterfaceClients().setDefaultScheme("https");
@Test
void shouldAddInterceptorWhenBaseUrlIsServiceIdUrl() {
Group group = new Group();
group.setBaseUrl("https://" + GROUP_NAME + "/path");
clientServiceProperties.getGroup().put(GROUP_NAME, group);
LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer(interceptorProvider,
clientServiceProperties, loadBalancerClientFactory);
LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer(
interceptorProvider, clientServiceProperties, loadBalancerClientFactory);
TestGroups groups = new TestGroups();
configurer.configureGroups(groups);
groups.builder.requestInterceptors(interceptors -> {
assertThat(interceptors).hasSize(1);
assertThat(interceptors.get(0)
.getClass()).isEqualTo(DeferringLoadBalancerInterceptor.class);
}
);
assertThat(interceptors).hasSize(1);
assertThat(interceptors.get(0).getClass()).isEqualTo(DeferringLoadBalancerInterceptor.class);
});
}
@Test
@@ -104,24 +101,19 @@ class LoadBalancerRestClientHttpServiceGroupConfigurerTests {
Group group = new Group();
group.setBaseUrl("https://some-other-service/path");
clientServiceProperties.getGroup().put(GROUP_NAME, group);
LoadBalancerRestClientHttpServiceGroupConfigurer configurer =
new LoadBalancerRestClientHttpServiceGroupConfigurer(interceptorProvider,
clientServiceProperties, loadBalancerClientFactory);
LoadBalancerRestClientHttpServiceGroupConfigurer configurer = new LoadBalancerRestClientHttpServiceGroupConfigurer(
interceptorProvider, clientServiceProperties, loadBalancerClientFactory);
TestGroups groups = new TestGroups();
configurer.configureGroups(groups);
groups.builder.requestInterceptors(
interceptors -> assertThat(interceptors).hasSize(0)
);
groups.builder.requestInterceptors(interceptors -> assertThat(interceptors).hasSize(0));
}
private static class TestGroups implements HttpServiceGroupConfigurer.Groups<RestClient.Builder> {
RestClient.Builder builder = RestClient.builder();
@Override
public HttpServiceGroupConfigurer.Groups<RestClient.Builder> filterByName(String... groupNames) {
throw new UnsupportedOperationException("Please, implement me.");
@@ -139,24 +131,27 @@ class LoadBalancerRestClientHttpServiceGroupConfigurerTests {
@Override
public void configureClient(BiConsumer<HttpServiceGroup, RestClient.Builder> clientConfigurer) {
clientConfigurer.accept(new TestGroup(GROUP_NAME, HttpServiceGroup.ClientType.REST_CLIENT, new HashSet<>()), builder);
clientConfigurer.accept(new TestGroup(GROUP_NAME, HttpServiceGroup.ClientType.REST_CLIENT, new HashSet<>()),
builder);
}
@Override
public void configureProxyFactory(BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
public void configureProxyFactory(
BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
}
@Override
public void configure(BiConsumer<HttpServiceGroup, RestClient.Builder> clientConfigurer, BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
public void configure(BiConsumer<HttpServiceGroup, RestClient.Builder> clientConfigurer,
BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
}
}
private record TestGroup(String name, ClientType clientType,
Set<Class<?>> httpServiceTypes)
implements HttpServiceGroup {
Set<Class<?>> httpServiceTypes) implements HttpServiceGroup {
}
}

View File

@@ -44,18 +44,21 @@ import static org.mockito.Mockito.when;
*
* @author Olga Maciaszek-Sharma
*/
@SuppressWarnings({"unchecked", "removal"})
@SuppressWarnings({ "unchecked", "removal" })
class LoadBalancerWebClientHttpServiceGroupConfigurerTests {
private static final String GROUP_NAME = "testService";
private ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;
private ReactiveHttpClientServiceProperties clientServiceProperties;
private ObjectProvider<DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction>> exchangeFilterFunctionProvider;
@BeforeEach
void setup() {
DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction> exchangeFilterFunction =
mock(DeferringLoadBalancerExchangeFilterFunction.class);
DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction> exchangeFilterFunction = mock(
DeferringLoadBalancerExchangeFilterFunction.class);
exchangeFilterFunctionProvider = new SimpleObjectProvider<>(exchangeFilterFunction);
loadBalancerClientFactory = mock(ReactiveLoadBalancer.Factory.class);
clientServiceProperties = new ReactiveHttpClientServiceProperties();
@@ -65,39 +68,33 @@ class LoadBalancerWebClientHttpServiceGroupConfigurerTests {
@Test
void shouldAddInterceptorWhenBaseUrlIsNotSet() {
LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer(exchangeFilterFunctionProvider,
clientServiceProperties, loadBalancerClientFactory);
LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer(
exchangeFilterFunctionProvider, clientServiceProperties, loadBalancerClientFactory);
TestGroups groups = new TestGroups();
configurer.configureGroups(groups);
groups.builder.filters(filterFunctions -> {
assertThat(filterFunctions).hasSize(1);
assertThat(filterFunctions.get(0)
.getClass()).isEqualTo(DeferringLoadBalancerExchangeFilterFunction.class);
}
);
assertThat(filterFunctions).hasSize(1);
assertThat(filterFunctions.get(0).getClass()).isEqualTo(DeferringLoadBalancerExchangeFilterFunction.class);
});
}
// properties.getInterfaceClients().setDefaultScheme("https");
@Test
void shouldAddInterceptorWhenBaseUrlIsServiceIdUrl() {
ReactiveHttpClientServiceProperties.Group group = new ReactiveHttpClientServiceProperties.Group();
group.setBaseUrl("https://" + GROUP_NAME + "/path");
clientServiceProperties.getGroup().put(GROUP_NAME, group);
LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer(exchangeFilterFunctionProvider,
clientServiceProperties, loadBalancerClientFactory);
LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer(
exchangeFilterFunctionProvider, clientServiceProperties, loadBalancerClientFactory);
TestGroups groups = new TestGroups();
configurer.configureGroups(groups);
groups.builder.filters(filterFunctions -> {
assertThat(filterFunctions).hasSize(1);
assertThat(filterFunctions.get(0)
.getClass()).isEqualTo(DeferringLoadBalancerExchangeFilterFunction.class);
}
);
assertThat(filterFunctions).hasSize(1);
assertThat(filterFunctions.get(0).getClass()).isEqualTo(DeferringLoadBalancerExchangeFilterFunction.class);
});
}
@Test
@@ -105,24 +102,19 @@ class LoadBalancerWebClientHttpServiceGroupConfigurerTests {
ReactiveHttpClientServiceProperties.Group group = new ReactiveHttpClientServiceProperties.Group();
group.setBaseUrl("https://some-other-service/path");
clientServiceProperties.getGroup().put(GROUP_NAME, group);
LoadBalancerWebClientHttpServiceGroupConfigurer configurer =
new LoadBalancerWebClientHttpServiceGroupConfigurer(exchangeFilterFunctionProvider,
clientServiceProperties, loadBalancerClientFactory);
LoadBalancerWebClientHttpServiceGroupConfigurer configurer = new LoadBalancerWebClientHttpServiceGroupConfigurer(
exchangeFilterFunctionProvider, clientServiceProperties, loadBalancerClientFactory);
TestGroups groups = new TestGroups();
configurer.configureGroups(groups);
groups.builder.filters(
filterFunctions -> assertThat(filterFunctions).hasSize(0)
);
groups.builder.filters(filterFunctions -> assertThat(filterFunctions).hasSize(0));
}
private static class TestGroups implements HttpServiceGroupConfigurer.Groups<WebClient.Builder> {
WebClient.Builder builder = WebClient.builder();
@Override
public HttpServiceGroupConfigurer.Groups<WebClient.Builder> filterByName(String... groupNames) {
throw new UnsupportedOperationException("Please, implement me.");
@@ -140,23 +132,27 @@ class LoadBalancerWebClientHttpServiceGroupConfigurerTests {
@Override
public void configureClient(BiConsumer<HttpServiceGroup, WebClient.Builder> clientConfigurer) {
clientConfigurer.accept(new TestGroup(GROUP_NAME, HttpServiceGroup.ClientType.WEB_CLIENT, new HashSet<>()), builder);
clientConfigurer.accept(new TestGroup(GROUP_NAME, HttpServiceGroup.ClientType.WEB_CLIENT, new HashSet<>()),
builder);
}
@Override
public void configureProxyFactory(BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
public void configureProxyFactory(
BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
}
@Override
public void configure(BiConsumer<HttpServiceGroup, WebClient.Builder> clientConfigurer, BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
public void configure(BiConsumer<HttpServiceGroup, WebClient.Builder> clientConfigurer,
BiConsumer<HttpServiceGroup, HttpServiceProxyFactory.Builder> proxyFactoryConfigurer) {
}
}
private record TestGroup(String name, ClientType clientType,
Set<Class<?>> httpServiceTypes)
implements HttpServiceGroup {
Set<Class<?>> httpServiceTypes) implements HttpServiceGroup {
}
}